
把一套 Agent 系统里最能打的一个模块抽出来单独做成一个开源 Skill是我最近做的事里性价比比较高的一个选择。原因很简单整套系统通常用来展示架构能力而单个 Skill 才是别人真正愿意拿去用的东西。系统越大别人迁移的成本越高Skill 越小别人接入的门槛越低。这里说的“电影感镜头”Skill最初是 Agent 导演系统里的一个模块负责把镜头类型、焦段、运镜方式、构图规则和光线氛围转换成生成模型能理解的指令。使用者反馈里出现频率最高的一句话是我不需要整套导演系统我只想把这段镜头语言能力接到自己的视频生成、分镜脚本或者摄影拆解流程里。于是我把这个模块从系统中剥离出来整理成独立 Skill 开源。这篇文章想讲清楚的问题是一个垂直能力从 Agent 系统中拆出来、变成独立开源 Skill标准做法到底是什么。拆哪些东西、留哪些依赖、目录怎么设计、代码怎么写、怎么验证、怎么再接入其它 Agent。如果你手上也有一堆写在 Prompt 里的“感觉”正好想把它变成可复用能力这篇文章能给你一条相对完整的路线。1. 先把问题说清楚Agent 系统为什么需要拆 Skill完整 Agent 系统之所以难复用问题几乎都出在耦合。一个典型的 Agent 导演系统会有调度模块、记忆模块、工具注册模块、权限控制、外部 API 接入甚至还有一套自己的配置中心。能力之间互相引用数据在模块之间流转。如果你只想把“镜头语言”这部分能力拿走你会发现它和分镜生成器绑定、和视频模型调用器绑定、甚至和用户输入解析器绑定。想单独使用必须先理解整套系统。拆出来单独开源本质上是切断这种耦合把能力变成一个“可独立交付的单元”。拆完之后的变化很明显第一依赖面从“整个 Agent 系统”降为“一个模型上下文 一个 Skill 目录”第二测试边界清晰了Skill 的输入输出可以被单独验证不再需要启动整个系统第三迭代风险变小改镜头库不会影响调度逻辑改调度逻辑也不会破坏镜头语言。当然不是所有能力都值得拆。一个模块是否适合拆成 Skill可以从三个维度判断能力边界是否清晰输入输出是否稳定跨项目复用的频率是否足够高。如果能力边界模糊输出格式随心情变化或者只在一个系统内部使用那它更适合继续待在系统里。判断标准不是“它很厉害”而是“别人能不能低成本地单独使用它”。2. Skill、Agent、工具与工作流四者的边界不能混很多开发者对 Skill 的理解是“一段 Prompt 打包放进目录”这容易在后续工程化时踩坑。先把概念边界理清楚。概念粒度是否自主运行生命周期典型示例Agent系统级是可自主决策长可多轮运行导演 Agent、客服 AgentWorkflow流程级半自主按固定流程走中一次任务分镜生成工作流Tool功能级否被调用即执行短单次调用视频生成 API、图片下载Skill能力级否为 Agent 提供知识和执行规则中可被多个任务复用电影感镜头 SkillSkill 的真正含义不是“一段提示词”而是一份“能力契约”。它规定了什么条件下使用、输入需要满足什么格式、输出应该长什么样、有哪些边界规则、需要附带哪些知识。Tool 只负责执行一个具体函数Skill 负责的是“知道什么时候该用这个函数、用的时候怎么组织输入、得到输出后怎么判断是否合理”。这也是为什么现在很多 Agent 框架里都开始出现 Skill 概念而不是把所有逻辑塞进 system prompt。system prompt 一旦变长模型对关键指令的注意力会被稀释Skill 机制则允许 Agent 按需加载相关能力保持主上下文精简。一个 Skill 目录通常由能力描述文件、提示词模板、辅助脚本和测试用例组成其中 SKILL.md 是入口也是 Agent 决定“要不要调用这个能力”的依据。3. 电影感镜头 Skill 到底封装了什么能力把“电影感”拆成可执行的规则是这个 Skill 的核心工作。如果只是一个“请你写得有电影感一点”的提示词模型输出的质量完全靠运气。真正的 Skill 需要把电影感拆解成机器可以理解、可以校验的知识结构。我把它分成三层来设计层次内容作用知识层镜头类型、焦段、运镜、构图、光线、色彩规则给模型判断“电影感”的客观依据模板层分镜脚本模板、视频生成提示词模板、导演备注模板统一输出格式降低模型自由发挥空间执行验证层脚本解析输入、生成镜头列表、校验参数合法性保证输出结构化参数不越界知识层是重点。镜头类型至少包含大全景、远景、全景、中景、近景、特写、大特写焦段建议在 16mm、24mm、35mm、50mm、85mm、100mm、135mm 这几个常用值中选并且要写明不同焦段带来的画面感受差异运镜方式包括固定、推、拉、摇、移、跟、升降、环绕、手持构图规则包括三分法、中心构图、框架构图、对称构图光线氛围包括金色时刻、蓝色时刻、伦勃朗光、低调照明等。模型只有在拿到这些枚举和规则之后才能稳定地产出“镜头语言正确”而不是“文字华丽但不专业”的内容。模板层解决的是输出规格问题。以前我在导演系统里直接让模型输出分镜结果格式每天都在变有时是表格有时是 JSON有时是散文。后来统一成模板之后模型的输出质量明显更稳定先填写场景、情绪目标和叙事关键点再按镜头编号逐条输出每个镜头包含景别、焦段、运镜、构图、光线和使用理由。模板的意义不是限制模型而是给模型一个稳定结构让它在结构里发挥。执行验证层则负责兜底。脚本层会校验镜头类型是否在允许列表内焦段是否属于常用值镜头数量是否超出预期。如果输入参数不合法直接报错而不是让模型硬答。这个设计在开源之后尤其重要因为使用者的项目各不相同一个能主动拒绝非法输入的 Skill比一个只会堆提示词的 Skill 可靠得多。4. 环境准备与 Skill 项目目录设计这个 Skill 对运行环境的要求很低。知识层和模板层是纯 Markdown 文件任何能编辑文本的环境都可以维护执行验证层我用了 Python 3.10 写辅助脚本不依赖任何第三方库所以不用特意准备虚拟环境。建议目录结构如下skills/cinematic-lens/ ├── SKILL.md # Skill 入口文件描述能力和使用方式 ├── prompts/ │ ├── shot_list_cn.md # 分镜脚本生成模板中文 │ └── video_prompt_en.md # 视频生成提示词模板英文适配主流视频模型 ├── scripts/ │ └── generate_shot_list.py # 生成镜头列表的辅助脚本 ├── tests/ │ ├── test_shot_list.py # 输出格式测试 │ └── fixtures/ │ └── sample_scene.json # 测试用输入样例 ├── examples/ │ ├── input_scene.json # 示例输入 │ └── output_shot_list.json # 示例输出 ├── README.md # 项目说明与接入文档 └── LICENSE # 开源协议SKILL.md 是整个 Skill 的核心入口。它不仅要让人类开发者看懂更要让 Agent 看懂。所以 description 字段要用“触发条件 能力范围 输出结果”的方式描述比如“当用户需要生成分镜脚本、视频生成提示词或者分析已有画面中使用到的镜头语言时使用本 Skill”。正文里要写清楚使用步骤、参数限制和输出规范全部用可执行的语气不要写模糊概念。scripts 和 tests 目录是工程化程度的分水岭。很多开源 Skill 只有提示词没有代码这会导致输出结构不可控。加入辅助脚本之后Skill 就从一个“文档”升级成了“小程序”可以单独运行、单独测试、单独接入 CI。tests 目录里的测试用例是我在实际维护中最受益的部分后面会详细展开。5. 核心实现最小可用的电影感镜头 Skill下面给出一套最小实现。第一个文件是 SKILL.md它决定了 Agent 如何理解和使用这个 Skill。# 文件路径skills/cinematic-lens/SKILL.md --- name: cinematic-lens description: 将电影镜头语言转化为结构化的分镜拍摄指令适用于视频生成提示词、分镜脚本编写、已有画面镜头语言分析等场景。 version: 1.0.0 license: MIT --- # 电影感镜头 Skill 当用户需要为一段剧情生成分镜、为视频生成模型编写带镜头语言的中英文提示词或者拆解一场戏的镜头构成时使用本能力。 ## 使用步骤 1. 获取场景描述包括剧情段落、情绪目标、关键人物关系、特殊符号或动作节点。 2. 调用示例脚本生成镜头序列脚本会输出带景别、焦段、运镜、构图、光线字段的结构化 JSON。 3. 对输出做合理性检查确认镜头数量在 3 到 8 个之间焦段使用常用值运镜方式在允许列表内。 4. 如需要英文视频提示词基于中文分镜 JSON 按 prompts/video_prompt_en.md 模板转换。 ## 边界 - 一次调用只处理一个场景段落不跨场景续写。 - 不生成音频、剪辑节奏、调色预设之外的执行建议。 - 输出 JSON 必须能被 json.loads 解析禁止输出解释性散文。第二个文件是分镜脚本模板提供稳定的输出格式参考。# 文件路径skills/cinematic-lens/prompts/shot_list_cn.md 你是一名资深电影摄影指导。请根据场景信息生成一份分镜脚本要求使用专业镜头语言输出为 JSON 数组。 场景描述{scene_description} 情绪目标{emotion_goal} 叙事关键点{story_beats} 输出要求 - 每个镜头包含 id、shot_type、focal_length、camera_move、composition、lighting、reason 七个字段。 - shot_type 只能从以下值中选择大远景、远景、全景、中景、近景、特写、大特写。 - focal_length 只能从以下值中选择16、24、35、50、85、100、135。 - camera_move 只能从以下值中选择固定、推、拉、摇、移、跟、升降、环绕、手持。 - 输出为 JSON 字符串不要包含任何解释性文字。第三个文件是辅助脚本负责生成结构化镜头列表。# 文件路径skills/cinematic-lens/scripts/generate_shot_list.py 根据场景描述生成带镜头语言的分镜清单输出 JSON 到标准输出。 import argparse import json import sys from dataclasses import dataclass SHOT_TYPES [大远景, 远景, 全景, 中景, 近景, 特写, 大特写] FOCAL_LENGTHS [16, 24, 35, 50, 85, 100, 135] CAMERA_MOVES [固定, 推, 拉, 摇, 移, 跟, 升降, 环绕, 手持] COMPOSITIONS [三分法, 中心构图, 框架构图, 对称构图] LIGHTINGS [金色时刻, 蓝色时刻, 伦勃朗光, 低调照明, 自然光] dataclass class Shot: shot_type: str focal_length: int camera_move: str composition: str lighting: str reason: str def build_shot(shot_type, focal, move, composition, lighting, reason): if shot_type not in SHOT_TYPES: raise ValueError(f不支持的景别: {shot_type}) if focal not in FOCAL_LENGTHS: raise ValueError(f不支持的焦段: {focal}) if move not in CAMERA_MOVES: raise ValueError(f不支持的运镜方式: {move}) return Shot( shot_typeshot_type, focal_lengthfocal, camera_movemove, compositioncomposition, lightinglighting, reasonreason, ) def main(): parser argparse.ArgumentParser(description生成电影感分镜清单) parser.add_argument(--scene, requiredTrue, help场景描述) parser.add_argument(--shots, typeint, default4, help镜头数量) args parser.parse_args() if args.shots 3 or args.shots 8: print(镜头数量建议控制在 3 到 8 之间脚本已退出。, filesys.stderr) sys.exit(1) preset [ (大远景, 24, 固定, 对称构图, 蓝色时刻, 建立场景空间关系交代环境与人物位置), (中景, 35, 移, 三分法, 自然光, 跟随人物动作保持叙事连续性), (近景, 85, 推, 中心构图, 伦勃朗光, 聚焦人物情绪揭示内心变化), (特写, 100, 固定, 框架构图, 低调照明, 突出关键细节制造视觉冲击), ] shots [] for i in range(args.shots): p preset[i % len(preset)] shots.append( { id: i 1, shot_type: p[0], focal_length: p[1], camera_move: p[2], composition: p[3], lighting: p[4], reason: f{p[5]}。场景上下文{args.scene}, } ) print(json.dumps(shots, ensure_asciiFalse, indent2)) if __name__ __main__: main()第四个文件是测试用例用来自动化验证输出结构。# 文件路径skills/cinematic-lens/tests/test_shot_list.py import json import subprocess import sys from pathlib import Path SCRIPT_PATH Path(__file__).parent.parent / scripts / generate_shot_list.py def run_script(scene雨夜街头对峙, shots4): result subprocess.run( [sys.executable, str(SCRIPT_PATH), --scene, scene, --shots, str(shots)], capture_outputTrue, textTrue, ) return result def test_output_is_valid_json(): result run_script() assert result.returncode 0 data json.loads(result.stdout) assert isinstance(data, list) assert len(data) 4 def test_shot_fields_are_valid(): data json.loads(run_script().stdout) for shot in data: assert shot[shot_type] in [大远景, 远景, 全景, 中景, 近景, 特写, 大特写] assert shot[focal_length] in [16, 24, 35, 50, 85, 100, 135] assert shot[camera_move] in [固定, 推, 拉, 摇, 移, 跟, 升降, 环绕, 手持]这套实现里的关键逻辑不在生成镜头本身而在校验。脚本先把不合法参数拦在入口处让模型或者调用者必须在受限集合内取值。测试用例再对脚本输出做一次结构检查形成双重保障。如果你后续要接入大型 Agent 框架这两步能帮你省掉大量排查时间。6. 把 Skill 接回 Agent三种集成方式Skill 开源之后使用者关心最多的问题就是怎么接入自己的 Agent。根据使用场景不同有三种常见集成方式按复杂程度从低到高排列。集成方式适用场景优点缺点上下文注入单能力、轻量 Agent接入快直接拼 system prompt占用上下文模型可能忽略边界函数调用需要结构化输出、需要脚本处理输出稳定可校验可回退需要写适配层知识库检索Skill 数量多需要按需召回主上下文精简需要维护索引和检索策略上下文注入是最快的方案直接把 SKILL.md 和 prompts 目录下的模板拼进 system prompt然后让模型按模板工作。但这个方式只适合能力单一、输入简单的场景因为一旦 SKILL.md 内容过长模型对关键约束的遵守率会下降。函数调用是更工程化的做法。把 generate_shot_list.py 注册成 Agent 的一个工具函数描述写清楚“什么情况下使用、输入参数是什么、输出结构是什么”Agent 在遇到相关请求时自动调用。这种方式最大的价值在于输出不再依赖模型对 JSON 格式的自觉而是脚本保证产出合法 JSON。如果脚本运行失败Agent 也能感知到错误并重新组织输入。下面是一个极简的接入示例演示如何让 Agent 框架加载 Skill 目录中的脚本。# 文件路径examples/integrate_with_agent.py import json import subprocess from pathlib import Path SKILL_SCRIPT Path(skills/cinematic-lens/scripts/generate_shot_list.py) def cinematic_lens_tool(scene: str, shots: int 4) - dict: Agent 可注册的电影感镜头工具输入场景描述返回结构化分镜 JSON。 result subprocess.run( [python, str(SKILL_SCRIPT), --scene, scene, --shots, str(shots)], capture_outputTrue, textTrue, ) if result.returncode ! 0: return {error: result.stderr.strip()} return json.loads(result.stdout) # 在 Agent 的工具列表中注册时描述要写明触发条件 TOOL_DESCRIPTION ( 当用户需要生成视频分镜、电影感镜头脚本 或包含景别焦段运镜构图光线的镜头序列时调用此工具。 )如果你的 Agent 框架本身支持目录式 Skill 插件也可以直接把整个skills/cinematic-lens目录挂载到框架约定的路径下让框架自动读取 SKILL.md 并注册脚本。不同框架的 Skill 目录格式略有差异但核心思路一致SKILL.md 负责描述能力scripts 负责执行测试负责保障。迁移时只需要改一层适配器不需要改动 Skill 内部代码。7. 运行结果与效果验证运行辅助脚本的命令如下cd skills/cinematic-lens python scripts/generate_shot_list.py --scene 雨夜街头对峙 --shots 4预期输出是一段合法的 JSON 数组[ { id: 1, shot_type: 大远景, focal_length: 24, camera_move: 固定, composition: 对称构图, lighting: 蓝色时刻, reason: 建立场景空间关系交代环境与人物位置。场景上下文雨夜街头对峙 }, { id: 2, shot_type: 中景, focal_length: 35, camera_move: 移, composition: 三分法, lighting: 自然光, reason: 跟随人物动作保持叙事连续性。场景上下文雨夜街头对峙 } ]判断一个镜头 Skill 是否真正有效不能只看输出格式还要看三个维度。第一个是格式合法性输出能否被json.loads成功解析字段是否齐全取值是否在枚举范围内。第二个是参数物理合理性焦段和景别的对应关系是否存在明显冲突比如用 135mm 拍全景虽然边界合法但画面逻辑牵强。第三个是叙事可用性镜头序列是否遵循了“先交代环境、再进入人物、最后强调细节”的常见叙事逻辑而不是随机排列。我在维护过程中用一组固定评测场景做回归包括“雨夜街头对峙”“清晨火车站离别”“实验室发现异常数据”等每个场景都要求输出 4 个镜头。只要某个改动导致这组场景中出现格式错误或专业错误就视为回归失败。对 Skill 这种 AI 相关项目来说固定评测集是最便宜的护城河因为模型输出天然有随机性没有评测集你就无法判断改动是变好了还是变坏了。8. 常见问题与排查思路在 Skill 开发和用户反馈过程中出现频率最高的问题通常集中在加载、格式、专业性和模型适配四个方面。问题现象可能原因排查方式解决方案Agent 没有触发 SkillSKILL.md 的 description 写得太宽泛或太窄检查 Agent 框架读取 Skill 描述的逻辑重写 description加入明确触发词和场景输出 JSON 解析失败模型在 JSON 前后加了说明文字查看原始输出和错误日志在提示词中强制“只输出 JSON”并在脚本层做重试镜头参数违反物理直觉知识层缺少约束枚举检查输出中的焦段和景别组合增加合法性校验脚本不合法直接报错视频模型忽略镜头指令镜头词被淹没在长提示词中对比不同提示词顺序的生成效果把景别和运镜词放在提示词关键位置多语言输出混乱模板未指定输出语言检查 prompt 中的语言说明在 SKILL.md 和模板中固定语言选项脚本依赖冲突使用者项目中已有同名依赖查看安装日志和依赖树尽量让脚本零依赖或用独立的 Python 环境这里最容易被低估的是第一项Agent 不触发 Skill。很多开源 Skill 项目把精力都放在写漂亮提示词上忽视了 description 的可匹配性。Agent 是依赖工具描述来决定调用策略的如果你的描述写得像产品宣传稿Agent 很可能在相关请求出现时没有任何反应。描述里应该直接写“当用户需要……时使用此 Skill”把触发条件放在第一位。另一个容易踩坑的地方是焦段和景别的搭配。长焦配近景很自然但 135mm 配大全景就会显得不专业。这种问题用枚举校验解决不了因为枚举只能保证单字段合法不能保证字段间关系合理。我的做法是在脚本里增加一条简单规则焦段大于等于 85mm 时景别不能是远景及更广的范围。规则不复杂但能拦住最离谱的输出。9. 开源 Skill 的工程化最佳实践开源一个 Skill 不是把文件夹传上去就结束了。GitHub 上大量 Skill 项目看起来结构完整实际用起来却问题百出核心原因是缺少工程化约束。这里给出几条我认为最重要的实践建议。第一SKILL.md 的 description 要面向机器优化。准备一个清单触发词优先、能力范围明确、输出格式点明、边界条件列清。不要写“这是一个强大的电影感镜头工具”这种空话而要写“当需要生成包含景别、焦段、运镜、构图和光线的分镜 JSON 时使用”。机器越容易判断什么场景该触发它Skill 的实际使用率越高。第二保留 examples 目录并持续维护。示例输入和示例输出是使用者理解 Skill 的最快方式也是你回归测试的依据。很多项目只写 README 不给示例导致使用者连跑通都困难。每个示例都要能从命令行一键复现输出结果要经过人工校对。第三测试用例要覆盖边界情况。至少包括正常场景、空输入、非法景别、非法焦段、超长镜头列表这几类。测试不需要覆盖所有枚举组合但要把最容易出错的分支覆盖住。我把测试接入项目的 CI 流程之后每次新增镜头规则都能快速发现是否破坏了已有功能。第四许可证要选清楚默认推荐 MIT 或 Apache-2.0。开源 Skill 通常包含 Markdown 和脚本两种内容Markdown 是文档脚本是代码两者许可可以一致但 README 里要写明版权归属和贡献方式。不要用无许可证的仓库那是实际使用者的法律风险黑洞。第五脚本遵循最小权限原则。辅助脚本只需要完成输入解析、参数校验、JSON 输出这三件事不要引入网络请求、文件删除、系统命令执行等能力。一个被 Agent 调用的 Skill 脚本理论上也是 Agent 工具链的一部分权限越大安全风险越高。使用者拿到的 Skill 应该开箱即用同时不产生额外的安全暴露面。第六版本管理要有节奏。SKILL.md 的 version 字段要跟着 release 走每次更新说明变更原因和影响范围。如果你改了提示词模板但没有改版本号使用者很难判断升级会不会影响他们的输出结果。建议在 README 里放一个简短的 CHANGELOG记录每次版本变更的关键内容。10. 总结与后续学习方向这次把电影感镜头 Skill 从 Agent 导演系统中拆出来开源整个过程最核心的收获不是代码而是确认了一条方法论一个能力要成为真正可复用的 Skill必须同时具备清晰的触发条件、稳定的输出结构、独立的验证方式和足够的工程化保障。缺少任何一个它都只是“一段有想法的提示词”而不是“一个可交付的能力”。Skill 项目的下一步可以考虑从三个方向继续深入。第一个方向是扩充知识层加入更多镜头语言维度比如轴线规则、正反打、视线匹配这类叙事镜头概念让模型输出的分镜具备更高级的叙事逻辑。第二个方向是建立更完整的评测集把固定场景扩展到不同类型剧情、不同情绪目标、不同时长要求用评测数据指导提示词迭代。第三个方向是适配更多生成引擎因为不同视频生成模型对镜头指令的响应方式差异很大一个 Skill 如果能提供多引擎适配层实用性会显著提升。最后给正在做类似开源项目的读者一个提醒不要把 Skill 当成一次性产物它是需要持续维护的“活项目”。框架版本会变、模型能力会变、使用者需求会变对应的评测集和示例也要跟着更新。真正让一个 Skill 产生价值的不是开源那一瞬间的曝光而是之后每一次基于真实反馈的迭代。建议收藏本文需要做 Skill 结构设计和工程化时随时回来对照。