news 2026/6/16 11:14:30

Spring Boot + LLM 工程化:把短视频流水线拆成 16 个独立角色的踩坑记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot + LLM 工程化:把短视频流水线拆成 16 个独立角色的踩坑记录

写在前面

如果你最近也在用 LLM 做内容流水线,大概率踩过这几个坑:

  1. prompt 链一长就脆,任何一步出错整条链报废
  2. 画面和字幕对不上,差 1-2 秒字幕就飘
  3. 改一个细节得重跑全流程,中间产物没落库
  4. 平台数据回写不知道怎么用

我业余时间做了一个开源项目Auteur,把这些问题逐个解决了。这篇文章把我踩过的坑和解决思路一次性写出来,代码 + 配置都是真实可跑的。

仓库地址:https://github.com/nxin-github/Auteur

在线 demo:https://nxin-github.github.io/Auteur/

技术栈:Spring Boot 3.3 + JPA + Flyway + MySQL 8.0 + Java 21 + Vue 3 + ffmpeg / Remotion


一、为什么不能用 prompt chain

最早做的就是 LangChain 风格的 chain:

brainstorm → script → storyboard → image → voice → video

跑了一周发现几个事:

  • LLM API 本来就不稳定,链越长越脆
  • 中间产物只在内存里,UI 看不到也没法人工介入
  • 改一处要全跑,5 分钟起步
  • 加自审到 chain 里要么改框架要么塞 if,丑

根本问题是 chain 把"流程"和"状态"耦合在一起。

后来推翻重做,每个 AI 角色一个独立 Spring Service,角色之间不直接互相调用,全部通过 DB 表解耦。读上游表,写下游表。

topic(选题) ↓ script(脚本) ↓ storyboard_shot(分镜,每镜一行) ↓ image_asset(图片资产) ↓ voice_asset(配音资产) ↓ video_asset(合成视频) ↓ published_video(平台回写的真实数据) ↓ weekly_review(周复盘)

每张表对应一个产物,任何一段失败可以单独重跑,不影响别的。中间产物全部落库,UI 上能看能改。

@ServicepublicclassScriptWriterService{// 读上游:topic 表// 写下游:script 表publicScriptResultwrite(LongtopicId){...}}@ServicepublicclassScriptCriticService{// 读:script 表// 写:script_review 表 + 触发 ScriptWriterService 重写一稿publicCriticResultreview(LongscriptId){...}}

加新角色 = 加新 Service + 新表,对其它角色零侵入。

⚠️ 踩坑:第二版我还试过用 Spring Events 解耦——不行,事件链一长跟 chain 没本质区别,异步事件链调试起来更难。回到"产物落库 + 显式触发",基于 DB 状态机才是正解。


二、自审反馈环:让 LLM 给自己批稿子

LLM 输出会漂移这是事实。常见处理方式:

  1. 换更大的模型 → 贵
  2. 写更细的 prompt → 反噬,prompt 越复杂 LLM 越抓不住重点
  3. 加 retry → 不知道为什么错

Auteur 的方案:给关键角色配一个自审角色。

publicScriptResultwriteWithCritic(LongtopicId,intthreshold){ScriptResultdraft=scriptWriter.write(topicId);CriticResultreview=scriptCritic.review(draft);if(review.score()<threshold){returnscriptWriter.rewriteWithFeedback(draft,review.feedback());}returndraft;}

调试这个 loop 时踩了三个坑,每个都让我返工过:

坑 1:自审 prompt 必须"找问题"导向不能"打分"导向。让 LLM 列出 3 个最大的问题,比让它给个 80 分有用得多。打分版本会出现"凑分数"——看见草稿写得不错就给个高分混过去。

坑 2:重写最多 1 次,再不行放过。我见过 LLM 把一个本来还行的剧本越改越奇怪,钻牛角尖比放过去还糟。

坑 3:自审反馈环必须是闭合的小循环,不能让自审失败传染下游。如果自审本身挂了(LLM 返回格式不对),跳过自审走原稿,不要让整个流水线挂在自审上。

目前编剧、摄影、美术三个角色都接了自审。生产跑下来编剧自审能把"逻辑断层"和"信息密度过低"两类问题捕住八成以上。


三、镜头时长锚定:画面与字幕一帧不差

这是我自己最得意的一个设计。

问题:普通流水线让 LLM 给每个镜头估时长(比如 3.5s),后端按这个时长拼图。但 LLM 估的时长跟真实 TTS 音频对不上,剪出来字幕飘 1-2 秒。我试过加更详细的 prompt 让 LLM 估准。没用,它压根不知道你的 TTS 模型每秒念几个字。

解法:让摄影指导给每个镜头一段 anchor_text(必须是脚本里的连续子串),后端 SRT 解析后在音频时间轴上反查这段文本的真实秒数:

publicDurationresolveShotDuration(Shotshot,List<SrtCue>srtCues){Stringanchor=normalize(shot.anchorText());SrtRangerange=srtCues.stream().filter(cue->normalize(cue.text()).contains(anchor)).findFirst().orElseThrow();returnDuration.between(range.start(),range.end());}

LLM 不再负责估算,只负责"指认"。LLM 擅长的是语义匹配,不是数值估算——把它放在它擅长的位置上。

校验链写得比较狠:

  • ✅ anchor 必须真的是脚本子串(normalize 之后比对,去标点 / 全半角 / 大小写)
  • ✅ 相邻 shot 的 anchor 在脚本里位置必须单调递增(防 LLM 把镜头顺序搞乱)
  • ⚠️ 没命中的镜头标anchor_match=false,视频还能渲,但日志和 UI 都会提示

踩坑:normalize 一定要包括去全半角和繁简体,不然 LLM 输出的 anchor 跟脚本对不上。我一开始只去了标点,匹配率惨不忍睹。


四、Agent 工具系统:自然语言驱动整条流水线

光有流水线还不够,用户得点 N 个按钮才能把活干完。所以又加了一个 Agent 聊天工作台。

底层是带工具调用 + 审批门槛 + Skill 上下文加载的对话循环。几个工程关键点:

1. 工具自动注册

@ComponentpublicclassStoryboardTools{@Tool(name="regenerate_image_for_shot",description="重新为指定 shot 生成图片")publicRunRefregenerateImageForShot(LongshotId,StringstylePatch){...}}

ToolRegistry启动时扫描所有@Tool注解,自动注册到 Agent 上下文,不用改 Agent 主循环代码。这是 Spring 注解扫描在 LLM 工程里的一个绝佳应用场景。

2. 写操作必须实现审批接口

publicinterfacePreviewableHandler<T>{PreviewCardpreview(Targs);// 给前端的审批卡Objectexecute(Targs);// 用户点确认后才跑}

前端弹一张"即将做这件事,确认吗"的卡片,用户按一下才执行。这是把 LLM 不可控性挡在副作用之外的最后一道闸。

踩坑:我一开始没加这个,调试时 Agent 自作主张把一个预设的 prompt 改了,改回去花了我半小时。从那以后所有写操作必须走审批门槛

3. Skill 按需加载,不是 system prompt 塞所有上下文

把"调整内容"“触发流水线”“创建选题”“改预设”“编辑文本” 5 类剧本写成 markdown 放在agent/skills/,Agent 自己根据当前对话决定加载哪份。

避免 system prompt 越写越长占满 context window。

4. 长任务异步 + runId 轮询

生图、合成视频这类任务要几十秒。Agent 工具返回runId,前端轮询GET /api/runs/{id}看进度。Agent 不阻塞在长任务上。


五、模型 ID 不在代码里写死

这是后期重构的一个关键决策。早期我把 LLM 模型 ID 散落在各 Service 里:

// ❌ 散落各处的字面量privatestaticfinalStringSCRIPT_MODEL="deepseek-chat";LlmCallSpec.builder().model("gpt-4o-mini").build();

后来改型号、做 A/B、降级模型时全是 grep 改代码。改成ModelRegistry:

@ServicepublicclassModelRegistry{publicStringmodelFor(Stringstep){...}publicStringmodelOrDefault(StringpresetValue,Stringstep){...}}

所有 LLM / 图像 / Agent 模型走ModelRegistry.modelFor("<step>")app_config表(category='model',key 形如auteur.model.<step>),前端"配置 → AI 模型"页面统一编辑。预设可覆盖的步骤(脚本/分镜/批评/脑暴/图像主模型)用modelOrDefault(presetValue, step),preset 优先,本页兜底。

加新流水线步骤的标准动作:

  1. 写下一个V*__model_<step>.sql迁移,INSERT IGNORE注册 +UPDATE COALESCE灌默认值
  2. ModelRegistry.KNOWN_STEPS列表里加上新 step,启动自检会校验
  3. Service 注入ModelRegistry,调modelFor(step)modelOrDefault(presetValue, step)
  4. 前端ModelConfig.vueGROUPS数组里把新 step 加进合适分组

六、本地优先 + 可降级

任何外部依赖缺失,后端不能挂。这条对开源项目特别重要——你不知道用户机器上装了啥。

依赖没配会怎样
火山 TOS走本地路径 +/api/files/...静态服务
火山 TTS配音 disabled,前端提示
Jamendo BGMBGM 不推荐,制片照常合成
Remotion走纯 ffmpeg 路径
LLM 网关OpenAI 兼容协议,vLLM / DeepSeek / 智谱 / Anthropic 都行

写起来到处都是兜底,但这事不能省。


七、几个让我返工过的踩坑

1..gitignore必须用前导/锚定

storage/递归忽略所有层级——不光忽略backend/storage/产物目录,还会一并忽略backend/src/main/java/com/auteur/storage/业务包。CI 编译失败你都不知道为啥。

正确写法:/storage/,只匹配 git 根下的同名目录。

.dockerignore语义不同(用 Gofilepath.Match不递归),所以同样写法在 dockerignore 里没问题——这种语义差异让我本地 docker build 通过、CI 编译挂。

改完.gitignoregit check-ignore -v <file>抽查关键源码目录验证。

2. Spring Bootddl-auto: validate严格校验

加字段忘 Flyway migration → 启动失败。强制走 Flyway,不要直接改 entity 让 JPA 自动建表。

正确做法:

  1. backend/src/main/resources/db/migration/V<下一个数字>__<描述>.sql
  2. 数字递增不能跳号,描述用下划线分隔
  3. 同时改 entity 类
  4. Flyway 启动时自动执行

3. alpine 容器里localhost优先解析 IPv6

但 nginx 默认只监听 IPv4 → healthcheck 永远失败。改成127.0.0.1立刻好。

4. Remotion 不支持file://协议

本地静态文件得走 HTTP URL,配auteur.video.remotion.public-base-url拼成http://host:port/api/files/...


数据回写驱动复盘

extension/目录是个 Chrome 扩展,插到抖音 / B 站 / 视频号 / 快手 的创作者后台,自动抓播放数和完播率 POST 回 Auteur,落published_video表。

WeeklyReviewService每周根据这些数据算哪些题材和钩子组合表现好,给下周的选题脑暴一份权重表。下一次选题策划就读这份周报。流水线越跑越懂受众。


怎么跑起来

gitclone https://github.com/nxin-github/Auteur&&cdAuteurcp.env.example .envdockercompose up-d--build

3-5 分钟,打开 http://localhost:5174,右上角切到 admin → 系统设置 → 填 LLM key(OpenAI 兼容协议都行),其它依赖留空就降级。


最后

完全开源 MIT 协议,随便用、随便改、随便商用。

  • 仓库:https://github.com/nxin-github/Auteur
  • 在线 demo:https://nxin-github.github.io/Auteur/
  • 给 AI 编程助手的 onboarding cheat sheet:CLAUDE.md(项目根目录)

LLM 工程化是个大坑,这个项目把我四个月里踩过的坑基本全暴露了。希望对你有用。

如果觉得思路有意思,star 是对独立开发者最直接的鼓励。有问题欢迎评论或者 GitHub issue。

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

终极指南:在Linux上完美运行哔哩哔哩客户端的3种简单方法

终极指南&#xff1a;在Linux上完美运行哔哩哔哩客户端的3种简单方法 【免费下载链接】bilibili-linux 基于哔哩哔哩官方客户端移植的Linux版本 支持漫游 项目地址: https://gitcode.com/gh_mirrors/bi/bilibili-linux 想在Linux桌面上享受完整的哔哩哔哩体验吗&#xf…

作者头像 李华
网站建设 2026/6/16 11:13:59

2026手机制作红底证件照保姆级教程,多款换背景软件手把手教学

急着报名考证、入职交材料需要红底证件照&#xff0c;线下照相馆排队花钱不说&#xff0c;拍出来还不一定满意&#xff1f;想自己用手机做红底证件照&#xff0c;却不知道靠谱的手机证件照换背景软件有哪些&#xff0c;抠图边缘发毛、底色不正、尺寸不合规&#xff0c;每次导出…

作者头像 李华
网站建设 2026/6/16 11:11:53

Web 渗透测试课程学习心得

一、前言这个学期系统学习了 Web 渗透测试课程&#xff0c;从基础的代理爬虫、身份会话测试&#xff0c;到文件包含、文件上传漏洞&#xff0c;再到手工 SQL 注入与 SQLMap 工具自动化注入&#xff0c;一步步从零接触网络安全攻防。过去我对网络安全的认知只停留在 “黑客攻击网…

作者头像 李华
网站建设 2026/6/16 11:10:51

uniapp获取App 应用版本信息

if(typeof plus ! undefined){plus.runtime.getProperty(plus.runtime.appid,function(versionInfo){let versionversionInfo.versionlet versionCodeversionInfo.versionCode}) }

作者头像 李华
网站建设 2026/6/16 11:09:50

Zotero插件市场:一站式插件管理终极解决方案

Zotero插件市场&#xff1a;一站式插件管理终极解决方案 【免费下载链接】zotero-addons Zotero Add-on Market | Zotero插件市场 | Browsing and installing plugins within Zotero 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-addons 你是否曾为寻找合适的Zo…

作者头像 李华