news 2026/5/9 4:27:14

OpenClaw AI模型切换器:Bash脚本实现无感模型切换

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenClaw AI模型切换器:Bash脚本实现无感模型切换

1. 项目概述:为OpenClaw打造一个轻量级AI模型切换器

在深度使用OpenClaw这类AI助手框架时,我经常遇到一个场景:同一个对话中,前半段需要Claude Opus来帮我进行复杂的逻辑推理和代码架构设计,后半段可能只需要Gemini Flash来快速生成一些简单的文本摘要或翻译。每次都要中断对话,去修改配置文件或者重启Agent,流程非常割裂,严重影响了“沉浸式”的AI协作体验。这个痛点促使我动手开发了OpenClaw Skill: Model Switcher,一个纯粹的、基于文本菜单的AI模型会话切换工具。

这个技能的核心价值在于“无感切换”。它允许你在一次OpenClaw对话会话中,通过几句简单的自然语言指令,实时、动态地将后续对话的AI大脑从一个模型切换到另一个,而无需任何界面点击或配置修改。整个技能采用Bash脚本实现,依赖极简(仅需jq),完全遵循OpenClaw的技能架构,安装即用。无论是开发者想对比不同模型对同一问题的回答差异,还是普通用户希望针对不同任务(如创意写作、代码审查、快速查询)调用最合适的模型,这个工具都能让模型切换变得像在餐厅换一道菜一样简单直接。

2. 核心设计思路与方案选型

2.1 为什么选择“纯文本菜单”而非交互式按钮?

在项目初期,我考虑过几种交互方案:图形化按钮、下拉菜单或是复杂的自然语言解析。最终选择纯文本编号菜单,是基于对OpenClaw使用场景的深度理解。

首先,OpenClaw本身是一个高度可定制、偏向开发者和极客用户的命令行AI助手框架。它的交互环境可能是终端、可能是简化的Web界面,也可能是通过其他消息平台桥接。在这种情况下,保证交互方式的“最低通用性”至关重要。纯文本编号菜单在任何支持文本输出的环境中都能完美渲染,无需考虑前端兼容性或特定平台的API限制。用户只需要“看”和“输入数字”,这是最原始也最可靠的交互方式。

其次,这降低了用户的使用心智负担。想象一下,当你说“列出模型”后,屏幕上出现一个清晰编号的列表,你不需要思考“我该点哪里”或者“这个按钮是什么意思”,直接回复“2”即可。这种交互模式借鉴了早期Unix系统工具和CLI程序的设计哲学,高效且不易出错。对于需要频繁切换模型的进阶用户来说,数字输入的速度远快于移动光标点击。

最后,从实现复杂度看,文本菜单使得技能逻辑极其清晰:生成菜单 -> 捕获用户数字回复 -> 执行切换。整个状态管理非常简单,不需要维护复杂的回调函数或会话状态,完美契合了OpenClaw技能应保持轻量、独立的设计原则。

2.2 技术栈决策:Bash + jq 的轻量化之道

市面上很多AI工具链和插件倾向于使用Python,因为它有丰富的AI生态库。但在这个技能中,我坚持使用Bash + jq的组合,这是经过深思熟虑的。

核心考量是依赖与性能。OpenClaw本身可能运行在各种环境,从云服务器到本地树莓派。要求用户额外安装Python及其包管理器,会增加部署的复杂性和潜在的环境冲突风险。而bash是几乎所有Unix-like系统的默认Shell,jq这个强大的JSON处理工具也几乎是现代Linux/macOS系统的标配(macOS通常预装,Linux一条apt install jqyum install jq即可)。这意味着技能在绝大多数目标环境中可以做到“零额外依赖”或仅需安装一个轻量级通用工具。

从功能上看,这个技能的核心操作是:1. 读取OpenClaw的JSON格式配置;2. 解析模型列表;3. 格式化输出文本;4. 根据输入调用OpenClaw API。jq在解析和过滤JSON数据方面能力超群,用一行命令就能完成复杂的查询和格式化,其效率远超启动一个Python解释器并加载json库。Bash脚本则负责流程控制、用户输入捕获和命令调用。两者结合,使得整个技能的响应速度极快,通常在毫秒级内就能完成菜单生成或模型切换。

注意:虽然Bash脚本在可读性和复杂逻辑处理上不如Python,但对于这种“胶水型”任务,它的简洁和高效是无可替代的。我们通过良好的脚本结构(如函数化)和注释,完全可以保证其可维护性。

2.3 别名系统的设计:从易用到高效

“别名”是这个技能提升用户体验的关键设计。OpenClaw的配置中,模型标识符可能是anthropic/claude-3-opus-20240229这样的长字符串,让用户记忆和输入它们是反人性的。

因此,我设计了一个双层级别名系统:

  1. 内置别名:技能会为一些知名模型创建通用、简短的别名。例如,将anthropic/claude-3-opus-20240229映射为opus,将google/gemini-2.0-flash-exp映射为gemini-flash。这些别名基于社区共识,开箱即用。
  2. 配置派生别名:技能会智能地从模型标识符中提取关键部分作为别名。例如,从google/gemini-2.0-pro-exp中提取gemini-pro。这个规则通常是通过截取模型ID中/后的部分,并去掉版本号等冗余信息来实现。

这样,用户切换模型就有了三种方式:

  • 按编号:最直观,看到菜单后输入数字。
  • 按完整模型ID:适合高级用户精确控制。
  • 按别名:最快捷,例如直接说“用opus”或“切换到gemini”。技能内部会将用户输入的别名映射回完整的模型ID。

这个设计极大地模糊了用户记忆成本,让自然语言指令(如“用那个快的模型”)通过别名映射成为可能,提升了交互的自然度。

3. 技能实现细节与核心脚本解析

3.1 技能目录结构与OpenClaw集成机制

OpenClaw的技能系统遵循约定大于配置的原则。我们将技能克隆到特定的目录后,OpenClaw会在启动时自动扫描并加载。

skills/model-switcher/ ├── README.md # 项目说明文档 ├── SKILL.md # 技能元数据文件,OpenClaw据此识别技能 └── scripts/ └── model-switcher.sh # 核心功能脚本

其中,SKILL.md文件是这个技能被OpenClaw识别的关键。它的内容通常如下:

--- name: Model Switcher description: Switch AI models in the current session via a text menu. version: 1.0.0 author: Your Name triggers: - switch model - 切换模型 - list models - use gemini - 用 flash script: scripts/model-switcher.sh ---

这个YAML前端元数据定义了技能的名称、描述、触发词和入口脚本。当用户在OpenClaw会话中输入任何triggers列表中的短语时,OpenClaw的核心引擎就会捕获这条消息,并调用指定的script(即我们的model-switcher.sh)来处理。

3.2 核心脚本model-switcher.sh分步解读

让我们深入核心脚本,看看它是如何工作的。以下是关键步骤的拆解:

第一步:获取与解析模型配置脚本首先需要知道系统中有哪些模型可用。它通过调用OpenClaw的命令行接口来获取配置。

#!/bin/bash # 通过OpenClaw CLI获取默认代理配置,其中包含模型列表 CONFIG_JSON=$(openclaw config get agents.defaults --format json) # 使用jq解析JSON,提取出模型列表。 # 假设配置中模型信息位于 `.models` 或 `.model` 字段,这是一个数组。 MODELS=$(echo "$CONFIG_JSON" | jq -r '.models[]?.id // .model? // empty')

这里用了jq的高级查询语法。-r参数输出纯文本(去掉JSON引号)。?操作符表示如果字段不存在则返回null而非报错。//是jq的默认值操作符,它尝试从.models[].id获取,如果失败则尝试.model,如果都为空则输出空。这增强了脚本的鲁棒性,以应对不同版本的OpenClaw配置格式。

第二步:构建模型菜单与别名映射获取到原始的模型ID列表(如["anthropic/claude-opus", "google/gemini-flash"])后,需要将其转换为用户友好的菜单。

# 初始化菜单文本和模型数组 MENU="🔄 模型切换菜单\n\n✨ 共有 ${#MODEL_ARRAY[@]} 个可用模型:\n\n" declare -A MODEL_MAP # 用于存储序号到模型ID的映射 declare -A ALIAS_MAP # 用于存储别名到模型ID的映射 INDEX=1 for MODEL_ID in $MODELS; do # 1. 存储映射 MODEL_MAP[$INDEX]=$MODEL_ID # 2. 生成别名 ALIAS="" # 规则1: 已知模型内置别名 case $MODEL_ID in *claude*opus*) ALIAS="opus" ;; *gemini*flash*) ALIAS="gemini-flash" ;; *gemini*pro*) ALIAS="gemini-pro" ;; # ... 其他规则 esac # 规则2: 从ID提取(例如从“provider/model-name”提取“model-name”) if [[ -z "$ALIAS" ]]; then ALIAS=$(echo "$MODEL_ID" | sed 's|.*/||' | sed 's/-[0-9].*$//') # 移除路径和版本号 fi ALIAS_MAP[$ALIAS]=$MODEL_ID # 3. 判断是否为默认模型(假设第一个是默认) DEFAULT_MARKER="" if [[ $INDEX -eq 1 ]]; then DEFAULT_MARKER=" (默认) ✨" fi # 4. 格式化菜单项 MENU+="${INDEX}️⃣ $MODEL_ID\n 别名: $ALIAS$DEFAULT_MARKER\n\n" ((INDEX++)) done # 添加重置选项 MENU+="──────────────\n\n0️⃣ 重置为默认模型\n\n💡 回复编号(0-${#MODEL_ARRAY[@]})或别名即可切换"

这段代码完成了多项任务:建立索引映射、智能生成别名、标记默认模型,并格式化为清晰的多行文本。declare -A创建了Bash的关联数组,这是实现快速别名查找的关键。

第三步:呈现菜单与捕获用户输入脚本需要将菜单输出给用户,并等待用户的下一步指令。在OpenClaw的技能上下文中,脚本的stdout会自动作为AI助手的回复发送给用户。

# 输出菜单 echo -e "$MENU" # 在OpenClaw技能中,通常不需要脚本自己读输入,因为用户的下一条消息会作为新触发。 # 但技能逻辑需要“记住”它正处于菜单状态。这通常通过OpenClaw的会话状态(session)或技能自己的状态机来实现。 # 一种简单实现是:本次调用只输出菜单,并期待用户的下一条消息是数字或别名。 # 更复杂的实现可能需要技能在后台保持一个“等待选择”的状态标志。

在实际实现中,为了处理用户随后的“数字”或“别名”回复,我们需要在SKILL.md中定义更广泛的触发词,或者利用OpenClaw的“对话状态”管理。一种常见模式是:当技能被“切换模型”触发时,它设置一个会话状态变量(如awaiting_model_selection=true)。然后,一个更通用的“消息拦截”技能会检查此状态,如果为真,则将用户的下一条消息(无论是数字还是文本)路由到本技能的另一个处理函数。

第四步:执行模型切换当技能确定用户选择了某个模型(通过数字或别名解析出最终的MODEL_ID)后,就需要调用OpenClaw的API来实际切换当前会话的模型。

# 假设通过某种方式(如状态机)获得了用户的选择 SELECTION TARGET_MODEL_ID="" if [[ "$SELECTION" =~ ^[0-9]+$ ]]; then # 用户输入的是数字 if [[ $SELECTION -eq 0 ]]; then TARGET_MODEL_ID="" # 重置为空表示默认 elif [[ -n "${MODEL_MAP[$SELECTION]}" ]]; then TARGET_MODEL_ID="${MODEL_MAP[$SELECTION]}" else echo "❌ 无效的编号。" exit 1 fi else # 用户输入的是文本(别名或完整ID) # 先检查别名映射 if [[ -n "${ALIAS_MAP[$SELECTION]}" ]]; then TARGET_MODEL_ID="${ALIAS_MAP[$SELECTION]}" else # 如果不是已知别名,则假定用户输入了完整的模型ID,直接使用(可做验证) TARGET_MODEL_ID="$SELECTION" fi fi # 调用OpenClaw API切换会话模型 # 假设OpenClaw提供了类似 `openclaw session switch-model` 的命令 if [[ -z "$TARGET_MODEL_ID" ]]; then openclaw session reset-model echo "✅ 已重置为默认模型。" else if openclaw session switch-model --model "$TARGET_MODEL_ID"; then echo "✅ 已切换模型至: $TARGET_MODEL_ID" else echo "❌ 模型切换失败,请检查模型ID是否正确或服务是否可用。" fi fi

这里的关键是错误处理。我们检查数字是否在有效范围内,检查别名是否存在,并在调用底层命令后检查其返回值,从而给用户明确的操作反馈。

4. 安装、配置与实战操作指南

4.1 环境准备与依赖检查

在安装技能之前,需要确保基础环境就绪。

  1. OpenClaw核心安装:你的系统上必须已经安装并配置好了OpenClaw。确保openclaw命令行工具可以全局访问,并且已经完成了基础的Provider(如Anthropic, Google AI Studio)的API密钥配置。你可以通过运行openclaw --version来验证。
  2. 安装jq:这是本技能唯一的硬性外部依赖。
    • macOS:通常已预装。如果没有,可通过Homebrew安装:brew install jq
    • Ubuntu/Debiansudo apt update && sudo apt install jq -y
    • CentOS/RHELsudo yum install jq -y
    • 验证安装:运行jq --version,应输出版本号。

实操心得:在服务器环境部署时,如果遇到权限问题,可以尝试将jq安装到用户目录,并确保~/.local/binPATH环境变量中。另外,有些极简Docker镜像可能没有jq,需要在构建镜像时显式安装。

4.2 技能安装的两种方式

方式一:克隆到技能目录(推荐)这是最标准的方式。OpenClaw通常有一个固定的技能搜索路径,最常见的是工作空间下的skills文件夹。

# 1. 导航到你的OpenClaw工作空间目录 cd ~/my-openclaw-workspace # 2. 确保存在skills目录,没有则创建 mkdir -p skills # 3. 克隆技能仓库 git clone https://github.com/rin4096/openclaw-skill-model-switch.git skills/model-switcher

完成后,重启你的OpenClaw Agent(或重新加载技能配置)。OpenClaw在启动时会自动扫描skills目录下的所有子文件夹,寻找SKILL.md文件并加载。

方式二:手动放置与软链接如果你的OpenClaw技能目录结构不同,或者你想将技能代码放在其他地方,可以使用软链接。

# 1. 将技能克隆到任意位置 git clone https://github.com/rin4096/openclaw-skill-model-switch.git ~/projects/oclaw-model-switch # 2. 创建软链接到OpenClaw的技能目录 ln -s ~/projects/oclaw-model-switch ~/my-openclaw-workspace/skills/model-switcher

这种方式便于你独立管理和更新技能代码,而不必侵入OpenClaw的工作空间。

4.3 配置你的模型列表

技能本身不需要额外配置,它直接读取OpenClaw的全局配置。因此,你需要确保OpenClaw的配置文件中已经定义了你想要切换的模型。

通常,OpenClaw的配置位于~/.config/openclaw/config.yaml或工作空间内的.openclaw/config.yaml。你需要编辑它,在agents.defaults部分(或类似结构)下添加models列表。

# 示例 config.yaml 片段 agents: defaults: model: anthropic/claude-3-opus-20240229 # 默认模型 models: # 可供切换的模型列表 - id: anthropic/claude-3-opus-20240229 # 可以在这里为每个模型添加元数据,如别名、最大token等(如果技能支持读取) - id: google/gemini-2.0-flash-exp - id: google/gemini-2.0-pro-exp - id: openai/gpt-4o - id: meta-llama/llama-3.3-70b-instruct

重要提示:技能脚本解析配置的逻辑是基于你实际的配置文件结构编写的。上述jq查询命令.models[].id需要与你的配置结构匹配。如果结构不同,你可能需要微调脚本中的jq查询语句。例如,如果你的模型列表是.agent.models,则需要相应修改。

4.4 完整使用流程演示

假设你已经安装好技能,并且配置了如上所示的4个模型。

  1. 启动对话:启动你的OpenClaw Agent,开始一个对话。
  2. 触发技能:在对话窗口中输入任意触发词,例如“switch model”“列出模型”
  3. 查看菜单:Agent会立即回复一个格式清晰的文本菜单:
    🔄 模型切换菜单 ✨ 共有 4 个可用模型: 1️⃣ anthropic/claude-3-opus-20240229 别名: opus (默认) ✨ 2️⃣ google/gemini-2.0-flash-exp 别名: gemini-flash 3️⃣ google/gemini-2.0-pro-exp 别名: gemini-pro 4️⃣ openai/gpt-4o 别名: gpt-4o ────────────── 0️⃣ 重置为默认模型 💡 回复编号(0-4)或别名即可切换
  4. 执行切换
    • 如果你想切换到Gemini Flash,可以直接回复“2”
    • 或者,你也可以直接说“用gemini-flash”
    • 技能会执行切换操作,并反馈:✅ 已切换模型至: google/gemini-2.0-flash-exp
  5. 验证切换:切换成功后,你接下来的所有对话(在当前会话内)都将由新选择的模型处理。你可以问一个问题,比如“你是谁?”,从回答的风格和内容上就能明显感觉到模型已经切换了。
  6. 重置模型:如果你想切回最初的默认模型,只需在菜单出现时回复“0”

5. 高级技巧、常见问题与故障排除

5.1 高级使用技巧

  1. 别名自定义:如果你对内置的别名生成规则不满意,可以直接修改model-switcher.sh脚本中的case语句。你可以为你的常用模型添加更简短的别名,比如将anthropic/claude-3-5-sonnet-20241022的别名设为sonnet,这样切换时只需说“用sonnet”。
  2. 结合快捷指令:你可以为常用的切换组合创建OpenClaw的快捷指令(Alias)或Shell别名。例如,在~/.bashrc中添加:
    alias to-gemini='echo "use gemini-flash" | openclaw chat'
    这样在终端输入to-gemini就能快速开启一个使用Gemini Flash的新会话。但这与技能提供的“会话内动态切换”是不同的场景。
  3. 模型分组:如果你有大量模型,可以考虑修改技能,使其支持分组显示。例如,在菜单中先显示“Claude系列”、“Gemini系列”、“开源系列”,让用户先选择组,再选择具体模型。这需要对脚本进行二次开发,增加层级菜单逻辑。

5.2 常见问题与解决方案速查表

问题现象可能原因解决方案
输入触发词后无反应,或提示“未找到命令”。1. 技能未正确安装到skills目录。
2. OpenClaw未重启/重载技能。
3.SKILL.md文件格式错误。
1. 检查skills/model-switcher/目录是否存在且包含SKILL.md
2. 重启OpenClaw Agent进程。
3. 检查SKILL.md的YAML语法,确保缩进正确,script路径无误。
菜单显示“没有可用模型”或模型列表为空。1. OpenClaw配置文件中未定义models列表。
2. 脚本中jq查询路径与你的实际配置不匹配。
1. 检查config.yaml,确保在正确位置定义了models数组。
2. 手动运行openclaw config get agents.defaults --format json查看输出,并调整脚本中的jq查询语句。
切换模型时失败,提示“模型切换失败”。1. 模型ID拼写错误或不存在于你的OpenClaw配置中。
2. 对应模型的API密钥未配置或已失效。
3. OpenClaw的session switch-model命令不存在或语法有变。
1. 仔细核对模型ID,确保与配置文件中完全一致。
2. 检查对应AI服务商(如Anthropic, Google)的API密钥配置。
3. 查阅你使用的OpenClaw版本文档,确认切换会话模型的正确CLI命令。
输入数字或别名后,技能没有执行切换,而是触发了其他操作或AI开始回答。OpenClaw的技能状态管理可能被其他流程中断。用户回复的消息没有被正确路由到本技能。这通常是OpenClaw技能系统交互逻辑的复杂性所致。一种更可靠的方法是让技能在输出菜单后,直接调用切换命令,而不是等待下一条消息。这需要修改技能逻辑为“一次性操作”:触发词 -> 显示菜单并同时要求用户在同一条指令中指定模型,例如“switch to model 2”。但这改变了交互模式。
脚本执行报错jq: command not found系统未安装jq按照上文“环境准备”部分安装jq

5.3 故障排查深度指南

如果遇到上表未涵盖的问题,可以按照以下步骤进行深度排查:

  1. 手动测试技能脚本

    cd ~/my-openclaw-workspace/skills/model-switcher bash -x scripts/model-switcher.sh

    使用-x参数运行脚本,它会打印出每一行执行的命令及其参数,这对于定位脚本中的逻辑错误或命令执行失败非常有用。观察输出,看在哪一步出现了非预期的结果或错误信息。

  2. 检查OpenClaw CLI可用性: 在脚本中,确保openclaw命令的路径是全局可访问的。有时在cron任务或某些上下文中,需要指定全路径(如/usr/local/bin/openclaw)。你可以在脚本开头添加which openclaw来检查。

  3. 验证JSON解析: 单独执行脚本中获取和解析配置的命令:

    CONFIG=$(openclaw config get agents.defaults --format json) echo "$CONFIG" | jq '.models'

    确保这条命令能输出你预期的模型数组。如果输出为空或报错,问题出在配置或jq查询上。

  4. 查看OpenClaw日志: OpenClaw在运行时会输出日志。查看日志文件(通常位于~/.cache/openclaw/logs/或工作空间的logs目录),搜索技能相关的错误信息。日志能告诉你技能是否被加载、触发时发生了什么。

  5. 技能交互模式再思考: 如前所述,最棘手的可能是“状态管理”问题。如果OpenClaw的技能框架不支持简单的“一问一答”式状态保持,那么“显示菜单->等待回复->执行切换”这个流程就可能失败。在这种情况下,一个更简单粗暴但稳定的改法是:将技能改为直接切换。即触发词必须包含目标信息,如“switch to model 2”或“use opus”。技能被触发后,直接解析命令中的模型标识并执行切换,无需中间菜单状态。这牺牲了一些交互友好性,但获得了100%的可靠性。你可以根据你的OpenClaw版本和实际需求,选择实现哪一种模式。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 4:25:01

从小学数学竖式到FPGA硬件:图解4位乘法器是如何‘搭’出来的

从小学数学竖式到FPGA硬件:图解4位乘法器是如何‘搭’出来的 记得小学三年级第一次接触乘法竖式时,老师用粉笔在黑板上画出的那些错位相加的格子吗?当时我们或许不会想到,这些看似简单的计算步骤,竟与当今最先进的芯片…

作者头像 李华
网站建设 2026/5/9 4:24:34

从零构建C++/OpenGL渲染引擎:核心架构、实现与调试指南

1. 项目概述:一个用C和OpenGL打造的轻量级渲染引擎最近在整理自己的代码库,翻出来一个几年前写的玩具项目,一个我称之为“CPlusPlusMiniEngine”的轻量级渲染引擎。它麻雀虽小,五脏俱全,核心目标就是用最纯粹的C和Open…

作者头像 李华
网站建设 2026/5/9 4:19:46

保姆级教程:用R语言复现HIV药物经济学Markov模型(附完整代码与数据)

从零构建HIV治疗Markov模型:R语言完整实现与可视化解析 在药物经济学领域,Markov模型就像一位精算师手中的水晶球,能够预测长期慢性病治疗过程中的健康状态变迁与资源消耗。想象你正面对一篇满是转移概率表格的文献,如何将这些静态…

作者头像 李华
网站建设 2026/5/9 4:18:10

为Godot引擎配置Catppuccin主题:提升开发体验的完整指南

1. 项目概述:为你的Godot引擎注入Catppuccin色彩 如果你和我一样,每天要花大量时间在Godot编辑器里敲代码、调节点,那么一个顺眼的编辑器主题就不仅仅是“锦上添花”,而是实实在在的生产力工具。长时间面对默认的、高对比度或者配…

作者头像 李华
网站建设 2026/5/9 4:13:30

基于Claude模型构建模块化AI技能库:架构设计与工程实践

1. 项目概述与核心价值 最近在AI应用开发圈里,一个名为“claude-skills”的项目引起了我的注意。这个项目名直译过来就是“Claude技能”,听起来像是一个围绕Anthropic公司Claude模型构建的工具集或技能库。作为一名长期混迹于AI工程化落地一线的开发者&a…

作者头像 李华
网站建设 2026/5/9 4:09:29

V-DPM技术解析:4D动态场景重建原理与实践

1. 项目概述V-DPM(Video Dynamic Point Map)这项技术最近在计算机视觉圈子里引起了不小的讨论。作为一名长期从事三维重建和动态场景分析的工程师,我第一次看到这个项目时就被它独特的思路吸引了。简单来说,这是一种能够从普通视频…

作者头像 李华