
今天拆的是 PenShot(story-shot-agent):一个把任意格式剧本切成 5 秒级镜头、并写出可直接喂给 Sora / Veo / Runway 的提示词包的 MIT 项目;文章给全它在链条上的位置、三条上手路径、八处已知阻塞,以及项目自己的源码考证里那些还没接线的一致性机制。
PenShot 是一个 Python 项目,仓库名 neopen/story-shot-agent,MIT 许可,LICENSE 上的版权署名是「Copyright (c) 2025 HiPeng (Peng)」,2025 年 10 月 23 日建仓。1 到 2026 年 9 月 26 日,仓库有 204 star、38 fork、594 次提交、15 个分支、17 个 tag 与 17 个 release;最新一版 v0.3.8 发布于 2026 年 9 月 19 日,同一天 PyPI 上的 penshot 也更新到 0.3.8。234fragments 数组里每个片段带 prompt(英文描述加中文描述各一段)、negative_prompt、duration、model、style,还有一个 audio 对象,写着音频提示词、目标音频模型、音色描述、情绪、语速与声道参数。5 成品视频不在里面,时间线也不在里面——这些字段是给下一个环节的输入。
Enhance PyPI upload workflow with manual trigger(接口记录的时间是 2026-09-19);文件列表里能看到 src/penshot、tests、examples、docs、scripts,以及一个 .agents/skills/penshot 目录。2
assets/imgs/penshot-uml-cn.webp(2026-09-26 从仓库取回):上方是三类接入方式(Python SDK、MCP Service、REST API / Docker),中间是 LangGraph 多智能体协作引擎,下方输出 Sora / Veo / Runway / 可灵提示词与环境音效、人声提示词;左侧那块「连续性保障系统」里并排写着 Chroma 向量检索、三级记忆与快照机制、角色 / 场景三视图 RAG——后两样今天在代码里的状态,见下一节。5global_metadata.character_outfits,指令转换时由固定函数拼成四段「全剧上下文」:本场景关键道具、角色服装要求、本场景地点、以及剧本级的连续性要点。12 三档提示词模板都把它定义成硬约束,其中一条原文是「参数矛盾优先采用【全剧上下文】设定」——镜头描述与全局基线打架时,以基线为准。记忆分三层:短期在 Redis 里做滑动窗口,中期用 LLM 压成摘要落盘,长期进 Chroma 向量库按剧本隔离。13style 去重后是否超过两种、相邻片段首尾时间差是否超过 0.1 秒。12 质量审查走的是规则优先的混合路线,评分从 100 分起按违规权重扣,warning 扣 5、moderate 扣 10、major 扣 25、critical 扣 40,扣到 0 分为止;两次真实运行的记录里,同一个剧本一次拿到 5.0 分、另一次拿到 90.0 分。14
三视图 / tri_view / turnaround / reference_image 零命中;连续性锚点的模型类 ContinuityAnchor 除定义处外无任何引用,状态快照的 take_snapshot 没有任何调用点,StateTimeline 没有工作流节点读取;RAG 那条路上,剧本知识库的 query() 收下 use_rerank 参数却不消费它,真正的重排序实现在另一条没接到主链路上的检索器里。12 也就是说,今天真正在跑的是文本基线、四段上下文注入、六项词表检查与扣分制审查这一套;参考图级别的锁形、跨片段的状态回放,属于还没通电路的部分。仓库自带的架构图里那三个圆柱体,其中一个还只是图纸。penshot 的 requires-python 写的是 >=3.10,仓库徽章也是 3.10+;官网技术架构那一栏写的是 Python 3.9+,两处口径不一致,装之前按 PyPI 的来。415LLM__DEFAULT 与备用厂商两段。516.env.example 默认指向 OpenAI 的嵌入接口,换成通义就把 base URL 改成 DashScope 并保留 text-embedding-v4。16project_info(片段数、总时长)和三条执行建议(按顺序生成、保持相同种子、生成后检查衔接)。5task_id,再轮询状态,终态的字面量是 success;源码里的任务状态枚举没有 completed 这个值,网上流传的示例常常写错。16breakdown_script,查询与管理的 get_task_status、get_task_result、cancel_task、list_tasks、get_queue_status、get_stats。16create_penshot_agent 加同步 / 异步两个方法)、REST API(FastAPI 起服务,/api/v1/storyboard 提交、/api/v1/status/{task_id} 查状态)、可以作为独立节点嵌进 LangGraph 工作流的 PenshotFunction,以及一个把整个服务包成 A2A Agent 的示例。517.agents/skills/penshot/SKILL.md,但它的描述写得很清楚——「PenShot 项目开发 Skill」,用来修改这个项目自己的 Agent、工作流、记忆、配置、REST、MCP 与测试,不是给外部 Agent 用来调用分镜功能的。18 要让 Agent 用它,走的是上面那七个 MCP 工具。.env、三行代码就够了,下面两段命令与代码取自仓库 README5:pip install penshot
cp .env.example .env # 填 PENSHOT_LLM__DEFAULT__* 与 PENSHOT_EMBED__DEFAULT__*,Redis 可选from penshot.api import create_penshot_agent
agent = create_penshot_agent(max_concurrent=5)
task_id = agent.breakdown_script_async("早晨,一个女孩在咖啡馆读书,阳光透过窗户……")
result = await agent.wait_for_result_async(task_id)docker-compose.yml 与 .env.example 之后 docker compose up -d,应用与 Redis 一起起来;不想写 compose 文件就用 docker run,把 PENSHOT_LLM__DEFAULT__*、PENSHOT_EMBED__DEFAULT__*、PENSHOT_REDIS_URL 用 -e 注进去即可,镜像在 ghcr.io/neopen/penshot:latest,Docker Hub 上另有 neotems/penshot(截至 2026-09-26 累计拉取 2,558 次,最后更新于 2026-09-19)。519python -m penshot.mcp_server --max-concurrent 5 --queue-size 500initialize → tools/list → tools/call 三步走,提交剧本拿到 task_id,轮询到 success 再取结果。16get_task_status 能读到当前阶段与各阶段进度,get_task_result 返回 success / status / data / error / 耗时 五样;16 仓库文档里一份 13 个片段、总时长 48 秒的示例输出带 processing_time_ms 约 519 秒,另一份同样是 13 片段的记录是 88,988 毫秒,两次相差近六倍。14deepseek-flash 在非高峰时段输入每百万 token 0.15 美元、输出 0.6 美元,高峰时段翻倍(高峰是 UTC 周一至周五 01:00–04:00 与 06:00–10:00)。20 项目自己给的口径是:语义分块与预处理砍掉 15%–25% 的输入 token,分级审查把 LLM 审查触发压到四成以下,三层记忆把单次调用的上下文从可能的上万 token 压到两千以内,提示词模板复用省下 50%–70% 的生成 token,综合下来整体省约 60%——这些数字来自项目自己的文档与实测记录,不是一个统一条件下的第三方基准。13text-embedding-v4 在阿里云百炼的官方计费页上是每千 token 0.0005 元。21enable_llm 关着。
现状:项目文档写得很直白——规则分割器按 2.5 秒左右均分,不判断动作与对话边界;只有超过阈值的镜头才交给 LLM 做语义分割。11
你能怎么办:要么接受规则模式的速度与零成本、把剧本写得段落分明,要么在配置里把 LLM 分割打开,让长镜头走语义路径;如果你要的是「切点必须落在动作停顿处」,那只有后一条路。duration 再做一次自己的校验,尤其是要喂给有硬时长限制的模型时。None,调用方要自己判空。1114 耗时的八成以上花在三段 LLM 环节上:镜头拆分、指令转换、质量审查。14
你能怎么办:一律走异步——提交拿 task_id,轮询状态,取结果;把超时值设成预期耗时的两三倍以上。breakdown_script 时不传 script,或者按自己猜的参数名调用,直接失败。
触发条件:用 stdio 方式接入 Claude Desktop、Cursor 这类客户端。
现状:这个 MCP 服务端没有用官方 SDK,而是用标准库手写了 JSON-RPC over stdio 循环,只实现 initialize / tools/list / tools/call 三个方法;tools/list 返回的七个工具参数 schema 全部是空对象,客户端看不到 script、language、wait 这些键名,只能凭 description 猜。协议版本写死成 0.1.0,规范里的通知(包括 notifications/initialized)一律回 -32601。同一批工具的 HTTP 版走 /tools 路由,参数描述是完整的。16
你能怎么办:接入时把参数名写进系统提示词里约束 Agent,或者先对着 HTTP 联调版确认契约;如果客户端对协议严格校验,先按作者自己的说法实测一遍兼容性。
/tools 才带完整参数描述」,第三条澄清任务终态的字面量是 success、不存在 completed。16redis_enabled 字段与实际连接地址;要横向扩容,先把记忆与缓存外置这件事当成前置条件。ContinuityAnchor、StateTimeline、GlobalConsistencyContract 有定义、无调用方;重排序有实现、没接在主链路上。今天在跑的六项检查是词表级比对,能发现「穿衬衫」变成「穿裙子」这类显性切换,对同义改述与方位错误无能为力。12
你能怎么办:把它的一致性预算定在「文本约束降低漂移概率」这一档。真需要锁形,得靠参考图或首尾帧条件生成这类下游能力,而这个仓库的边界就是提示词与指令编排。12docker run 示例或 compose 文件部署。
现状:示例里 Redis 的密码是 123456,docker-compose.yml 同样写着这个默认值——本地开发无所谓,生产环境必须换成随机值并用密钥管理;517 另外,任务输出、记忆文件与工作流检查点统一落在数据目录下,而这个目录不随源码发布,容器重建时要单独备份,否则中断恢复能力静默消失。14
你能怎么办:把 Redis 密码换成环境变量或密钥,把数据目录挂成持久卷并纳入备份。
>=3.10。415PengLine 3 次,两个机器人各 1 次;2 issue 列表里只有一个未关闭的 issue,是作者 2026 年 9 月 18 日开的讨论帖,正文只有一句「有问题可在此处讨论」,另外 5 个未关闭的 PR 全是依赖升级机器人开的。2223 而上面那些架构、性能与成本数字,几乎全部来自项目自己的技术文档站与源码测评——它写得比多数项目的文档都细,还主动标了工程短板,但它终究是项目一方自己的核验,其中那份总测评给出的 8.5 / 10 也是这个项目给自己打的分。17 读这些数字时,把它们当作者的实测记录,而不是第三方复现。| 方案 | 你给它什么 | 它给你什么 | 它在这一层 | 许可与公开体量 |
|---|---|---|---|---|
| 本期:PenShot(story-shot-agent) | 一份任意格式的剧本、一个对话模型 key 与一个嵌入模型 key,Redis 可选 | 镜头级中英双语提示词、负面词、片长与音频提示词 | 剧本到分镜的规划层 | MIT,204 star2 |
| video-shotcraft | 一份产品卖点与 157 张镜头配方卡 | 一条排好的宣传片时间线 | 镜头配方与素材资产 | Apache-2.0,9,558 star10 |
| vibeframe | Seedance / Runway / Veo / Kling 各自的 key | 一条带硬性花费上限的生成命令 | 多供应商生成路由 | MIT,170 star24 |
| descript-mcp | 一个 Descript 账号 | 改文字就是剪片的那套操作 | 官方 SaaS 的 MCP 接口 | Apache-2.0,32 star25 |
| Comfy MCP | 一个 Comfy 账号,或一台跑着 ComfyUI 的机器 | Agent 自己建图、跑渲染、批量取素材 | 生成引擎的官方接口 | AGPL-3.0-or-later 或商用许可,237 star26 |
| capcut-cli | Node 与一份本机剪映草稿库 | 一份还能继续改的工程 | 工程文件读写层 | MIT,742 star8 |
| Nomi | 一台机器与外部模型来源 | 一份本地工程:画布、分镜、时间线、MP4 | 本机工作台 | AGPL-3.0,523 star9 |
| HyperFrames | HTML 与组件代码 | 浏览器里的渲染底座 | 渲染底座 | Apache-2.0,53,073 star6 |
| hypit | 一份需求描述 | 一条从脚本到成片的流程 | 编排层 | 自订许可,15,973 star7 |
.env 里的对话模型与嵌入模型分开配好,先用一段两分钟以内的中文剧本跑一次异步任务;再拿这次运行的质量分、违规明细与连续性报告当基准,之后每次改提示词模板或换模型都跟它比一次。质量分是扣分制的终审快照,同一个剧本两次运行可以差出十几倍,不看明细只看总分一定会误判。14github.com
github.com
github.com
pypi.org
github.com
github.com
github.com
github.com
github.com
github.com
pengline.cn
pengline.cn
pengline.cn
pengline.cn
shot.helpenx.com
pengline.cn
pengline.cn
github.com
hub.docker.com
api-docs.deepseek.com
help.aliyun.com
github.com
github.com
github.com
github.com
github.com
This story was produced automatically by a channel. One sentence is all it takes for Neodrop to keep producing for you.