📖 漫剧 AGENT · 文档中心 ← 文档中心 首页 产线

Agent 生产系统开发文档

面向开发/测试:架构、模块契约、API、部署、测试用例与 FAQ。

漫剧 Agent 生产系统 · 开发文档

项 值
版本 v1.0
日期 2026-10-31
读者 开发 / 测试 / 运维
生产环境 47.108.208.23 · /opt/dsp-matrix-hq · dsp-hq.service(:8010)
代码位置 服务端 /opt/dsp-matrix-hq/app/backend/app/drama/ ;本地部署暂存 D:\八仙矩阵总部\_drama_deploy\
在线入口 https://hq.weixiaoyao.cn/static/drama.html(登录 hq 域后使用)

目录

  1. 系统概述
  2. 架构设计
  3. 核心模块详解
  4. 数据流与接口定义
  5. 技术选型
  6. 部署方案
  7. 开发进度计划
  8. 测试说明
  9. 常见问题与排障(FAQ)
  10. 扩展指南

1. 系统概述

1.1 定位

把「小说原文 / 题材梗概」自动变成「可发布的竖屏漫剧成片」的 多 Agent 流水线。输入一段文字,输出 final.mp4(含配音、字幕、分镜画面)+ 完整生产留痕(每个阶段的 JSON 产物、所用技能、实际模型)。

产品铁律(不可破,开发写代码前必读): 1. 永不自动发布——系统只产素材/成片,发布走人工(可衔接 publish_tasks 队列,入队≠发布)。 2. 数字回源——所有对外数字只信工具层/库返回值,不编造。 3. AI 与业务隔离——LLM 只经 llm.complete() 网关,不直连业务库。 4. 单段失败留痕中止——失败不吞错,drama_stage_runs.error 记录后停止,不硬跑后续段。

1.2 功能范围(In Scope)

能力 说明
剧本生成 小说/题材 → 结构化剧本(剧名/钩子/场次/台词/角色)
分镜编排 剧本 → 分镜表 + 全局画风 + 视频提示词
角色管理 角色小传 → 视觉锚定 + AI 定妆照(图库可用时)
配音合成 台词 → 情绪配音单 → TTS 实测落盘(.mp3 + 实测时长)
视频渲染 分镜提示词 × 角色锚定 → 逐镜出画 → 拼接 → 混音/字幕 → final.mp4
质检 违禁词/夸大承诺/侵权/限流风险体检,出分数与整改建议
技能台账 GET /api/drama/skills 可查每段用了哪个产线技能

1.3 不做(Out of Scope,有意排除)

1.4 与周边系统的关系

小说来源(番茄授权/自写) ──► 【漫剧产线 app/drama】──► final.mp4 + 素材包
                                   │
                                   ├─► publish_tasks(人工确认后发布矩阵)
                                   └─► cps_links/cps_commissions(软广植入变现台账,另模块)

2. 架构设计

2.1 四层架构

层 组件 职责
接入层 web/drama.html + routers/drama.py(/api/drama/*) 建项目/跑产线/看产物;bxsid 会话鉴权,写操作走 router_write_guard
编排层 drama/pipeline.py 六段顺序执行、产物传递(ctx 字典)、单段失败中止、任意区段可重跑、逐段落库
Agent 层 drama/agents.py + drama/skill_loader.py 6 个智能节点;每节点 = 产线技能提示词 + 任务指令 + JSON 输出契约 + LLM
能力底座 agent/llm.py(LLM 网关)、services/media_service.py(TTS/出画/合成)、db.py(SQLite WAL) 零改造复用既有矩阵总部底座

2.2 目录结构(服务端)

/opt/dsp-matrix-hq/app/backend/
├── app/
│   ├── main.py                    # 入口(补丁注册 drama_router + create_all 建表)
│   ├── drama/                     # ★ 漫剧产线核心包
│   │   ├── __init__.py
│   │   ├── skill_loader.py        # 产线技能加载(产线与技能中台的唯一集成点)
│   │   ├── agents.py              # 6 个 Agent 定义(指令 + JSON 契约)
│   │   ├── pipeline.py            # 六段编排 + 各段执行函数
│   │   ├── tables.py              # drama_projects / drama_stage_runs
│   │   └── skills/                # 13 份自研技能 SKILL.md(按段分目录)
│   │       ├── script/    SKILL.md + AUX_漫剧创作助手v3.md + AUX_短视频脚本大师.md
│   │       ├── storyboard/ SKILL.md + AUX_漫剧分镜技能.md + AUX_漫剧制片浪客.md
│   │       ├── character/ SKILL.md + AUX_3d影视动画漫画生成.md
│   │       ├── voice/     SKILL.md
│   │       ├── video/     SKILL.md + AUX_漫剧全链路制作.md
│   │       └── qc/        SKILL.md + AUX_多平台违禁词查询.md
│   └── routers/drama.py           # REST API
└── web/drama.html                 # 产线控制台 SPA

产物目录(数据,不入库):

/opt/dsp-matrix-hq/data/drama/<project_id>/
├── characters/   定妆照 <角色名>.png(1024x1536)
├── voice/        d1.mp3, d2.mp3, ...(TTS 逐句)
├── video/        bg.mp4(出画拼接)/ subs.ass(字幕)/ final.mp4(成片)
├── prompts/      <stage>-<时间戳>.txt(完整 prompt 快照,可复现留痕)
└── placeholder.png(无定妆照时的兜底图)

2.3 进程与并发模型


3. 核心模块详解

3.1 skill_loader.py — 技能加载器(集成点)

这是产线与「总部自研技能中台」的唯一集成点。 技能本质是角色提示词包(SKILL.md 全文)。

load_bundle(stage: str) -> str
# 返回该段完整角色提示词 = 主技能 SKILL.md 全文 + 各 AUX_*.md(带【辅助技能】分隔头)
# 抛 ValueError(stage 非法) / FileNotFoundError(技能目录缺失)

台账常量: - STAGE_ORDER = ("script", "storyboard", "character", "voice", "video", "qc") - STAGE_SKILLS: dict[stage → {agent, primary, aux[]}] —— 供 /api/drama/skills 与前端展示。

调用方式(agents.build_messages):system = load_bundle(stage) + 【本节点任务】instruction + 【输出 JSON 契约】output_spec + 【输出铁律】,user = 上游产物 JSON。注入 llm.complete(messages=...)。

替换/升级技能:直接覆盖 skills/<stage>/SKILL.md 或增删 AUX_*.md,重启服务即生效(load_bundle 每次读盘)。文件来源:八仙矩阵总部自研技能(source: baxian-matrix)。

技能台账(当前部署):

段 Agent 主技能 辅助技能
script 剧本创作 AI小说推文漫剧生成 漫剧创作助手V3、短视频脚本大师
storyboard 分镜生成 AI视频大师级分镜 漫剧分镜技能、AI漫剧制片-浪客
character 角色设计 角色特征提取技能 3D影视动画漫画生成
voice 配音合成 AI漫剧配音台词生成 —
video 视频合成 AI漫剧梦工厂 漫剧全链路制作技能
qc 质检 AI短剧全流程质检 多平台违禁词查询

3.2 agents.py — Agent 定义与输出契约

数据类 DramaAgent{key, name, tier, max_tokens, instruction, output_spec};AGENTS 字典注册 6 节点。tier 传给 LLM 网关选档(large=旗舰,medium=快省)。

输出铁律(拼进每条 system):只输出一个 JSON 对象、不要 markdown 代码块、字段必须存在、拿不到留空字符串、绝不编造。

各段 JSON 契约(output_spec,开发对接/测试断言以此为准):

script(tier=large, max_tokens=7000)

{"title":"剧名(≤12字)","logline":"一句话梗概(≤40字)","hook":"开头钩子句",
 "endcard":"片尾字卡(≤8字)","style_tone":"风格基调(≤20字)",
 "characters":[{"name":"","desc":"人物小传(≤40字)","voice_role":"narrator|hexiangu|lvdongbin|yuan|xiaoyu"}],
 "scenes":[{"scene_id":1,"setting":"场景(≤15字)","action":"动作描述(≤40字)",
   "dialogues":[{"role":"","line":"台词(≤18字)","emotion":"happy|sad|angry|fearful|surprised|neutral"}]}]}

storyboard(large, 7000)

{"visual_style":"全局画风锚定(≤60字)",
 "shots":[{"shot_id":1,"scene_id":1,"duration":5,"camera":"景别与运镜(≤15字)",
   "visual_prompt":"视频提示词(60~120字)","subtitle_hint":"对应台词"}]}

约束:每场 1~3 镜、全片 3~6 镜、每镜 4~8 秒;提示词只写可见元素(主体+动作+环境+镜头+光线+画风),禁止台词文字与品牌名。

character(medium, 4000)

{"style_prompt":"全局画风(≤40字)",
 "characters":[{"name":"","voice_role":"音色键",
   "visual_anchor":"视觉锚定(50~100字:发色/瞳色/服装/配饰/体型)",
   "portrait_prompt":"定妆照提示词(≤80字)"}]}

visual_anchor 是全片角色一致性的关键——video 段每条提示词都会复述它。

voice(medium, 5000)

{"lines":[{"role":"音色键","text":"配音句(≤18字,用…表示停顿)",
   "emotion":"happy|sad|angry|fearful|surprised|neutral"}]}

video(medium, 4000)

{"prompts":["最终镜头提示词(80~150字) = 角色锚定 + 本镜动作 + 竖屏9:16 + 画风 + 画面无文字"],
 "total_seconds":30}

qc(medium, 4000)

{"pass":true,"score":85,
 "issues":[{"level":"error|warn|info","stage":"script|storyboard|voice|video",
   "text":"问题描述","suggestion":"整改建议"}],
 "compliance_note":"一句话合规结论"}

3.3 pipeline.py — 六段编排

主编排:

async def run_project(project_id: int, from_stage: str | None = None,
                      to_stage: str | None = None) -> dict
# 返回 {"project_id", "stages":[{"stage","status","model","error","seconds"}], "status"}

执行语义(测试必读): 1. 阶段序列 = STAGE_ORDER 切片 [from..to](都可空 = 全链);from_stage > to_stage 顺序非法 → 400 拒绝。 2. 上游产物复用:开跑前从 drama_stage_runs 里取每段最近一条 status=ok 的 output_json 装进 ctx——所以可跳段跑(如只补跑 video),但上游缺产物的段会返回 error 而非抛异常(防御 .get,2026-10-31 修复)。 3. 并发与残留防护(2026-10-31 加固): - 触发端用原子条件 UPDATE 抢占(status!='running' OR updated_at<超时线),杜绝「查完再改」竞态双跑;running 且新鲜 → 409,running 但超时(>30 分钟)→ 允许接管重跑; - 每段开工刷新 updated_at(心跳),长段出画属正常窗口不会被误接管; - 进程启动即清扫(路由模块 import 时执行):BackgroundTasks 随进程消亡,遗留 running 一律复位 error(段记录标注「进程重启中断」)——防重启后项目永久卡死。 4. 每段执行:先插 status=running 记录 → 跑 _STAGERS[st](ctx) → ok 则写 output_json/model/artifact_path;异常则写 error = "ExcType: msg[:400]" 并中止后续段。 5. 可复现留痕:每段把完整 prompt(system=技能包全文+指令+契约 / user=上游产物)快照到 <project_dir>/prompts/<stage>-<时间戳>.txt;input_json 记录 skill_hash(技能包内容指纹)——换技能后历史运行仍可溯源。 6. 收尾回写 drama_projects:全部 ok → status=done,result_video ← video 段 video_path,result_summary ← qc 段 compliance_note,标题未命名时用 script.title;任一段失败 → status=error。

ctx(阶段间数据总线)结构:

ctx = {
  "project_id": int, "source_text": str, "style": str,   # 项目初始
  "script": {...}, "storyboard": {...}, "character": {...},
  "voice": {...}, "video": {...}, "qc": {...},           # 各段 output_json 解析后
}

各段执行函数行为:

函数 输入(ctx) 确定性动作(LLM 之后) 产出
_stage_script source_text[:6000] + style 无 剧本 JSON
_stage_storyboard script 无;script 缺 scenes → 返回 error 分镜表
_stage_character script.characters 图库可用时逐角色 generate_image(≤4 人,1024x1536),失败记 portrait_error 不阻塞 角色锚定 + 定妆照
_stage_voice script.scenes.dialogues + 角色音色表 LLM 配音单为空时降级直接取剧本对话;M.synth_lines TTS 全引擎降级落盘 {lines:[实测合成结果], raw_lines:[配音单]}
_stage_video storyboard + character + script.endcard prompts 空则降级用分镜 visual_prompt;generate_bg_video 出画拼接;plan_timeline+build_ass+compose 混音合成 {prompts_used, bg_video, video_path}
_stage_qc script+storyboard+voice 全链摘要 无 质检报告(issues+score+pass)

_run_llm_stage(agent, user_content) 统一封装:llm.complete(build_messages(...), tier, temperature=0.6, max_tokens=agent.max_tokens) → _parse_json_obj(res.text)。LLM 不可用时返回 {"error":"LLM 不可用(未配 Key)"} 而非崩溃。

同步包装 run_project_sync(project_id, **kw) 供脚本/测试直接调。

3.4 tables.py — 数据表

建表机制:SQLModel 定义 → main.py import 注册 metadata → db.init_db() 的 create_all 自动建(加列走 _auto_add_columns 自动 ALTER,不用写迁移)。

drama_projects(一部剧一条):

列 类型 说明
id int PK 项目 id(产物目录名)
tenant_id / created_by int 租户/创建者
title str 剧名(可由 script 段回填)
source_text str 小说原文/题材(≤20000)
style str 画风偏好,默认「国漫」
status str created | running | done | error
current_stage str 最后执行的段
result_video str 成片路径
result_summary str 质检一句话结论
run_count int 累计触发产线次数(成本护栏,超 30 次拒绝)
created_at / updated_at datetime UTC

drama_stage_runs(每段执行一条,全程留痕):

列 说明
project_id / stage / status 项目、段键、running|ok|error
skill_used 产线技能名(集成台账)
model 实际命中的 LLM/引擎
input_json / output_json 输入摘要 / 产物 JSON 全文
artifact_path 本段产物目录
error 失败原因(截 400 字)
started_at / finished_at UTC 时间

3.5 routers/drama.py — REST 层

见 §4.2。安全约定:全部端点经 bxsid 会话 + current_user/tenant_of 鉴权、租户隔离;写操作由主入口 router_write_guard 纳管;文件下载防路径穿越(拒 /、\、..)。

3.6 web/drama.html — 产线控制台

单页 SPA:项目列表 → 建项目(题材粘贴/画风选择)→ 一键全链 / 分段跑 → 六段进度卡(状态/模型/耗时/错误)→ 产物查看(成片播放、定妆照、配音单、质检报告)→ 技能台账。纯静态,挂 /static/drama.html,API 走同域 /api/drama/*。


4. 数据流与接口定义

4.1 全链数据流

source_text ──[script]──► 剧本JSON ──┬──[storyboard]──► 分镜表 ──────────┐
                                     ├──[character]──► 角色锚定+定妆照 ──┤
                                     └──[voice]──► 配音单 ──TTS──► .mp3 ─┤
                                                                          ▼
                                     [video] = 分镜prompts×角色锚定 → 出画 → 拼接
                                              → 时间轴+字幕ass+compose → final.mp4
                                                                          ▼
                                     [qc] = 全链摘要 → 违禁词/限流/侵权体检 → 报告

LLM 产物一律走「JSON 契约 → _parse_json_obj → ctx」;媒体产物一律走 media_service 落盘到 /data/drama/<id>/<stage>/。

4.2 REST API(/api/drama,全部需登录 bxsid)

方法 路径 入参 返回/说明
GET /skills — 技能台账:每段 agent/primary/aux
GET /projects — 项目列表(按租户过滤)
POST /projects {"title"?, "source_text"(1~20000字,必填), "style"?: "国漫"} {"id": ...}
GET /projects/{pid} — {"id","title","status","current_stage","result_video","result_summary","stages":[{stage,status,model,error,skill_used,output_json,artifact_path,...}]}
POST /projects/{pid}/run {"from_stage"?, "to_stage"?, "force"?: false} {"status":"running"}(BackgroundTasks 异步跑)。错误码:段名非法/段序颠倒→400;成本护栏:video 段已出过成片再跑须 force=true(否则 400)、单项目 run 次数超 30→400;项目不存在/跨租户→404;产线运行中(未超时)→409;超时残留自动接管
GET /projects/{pid}/file ?name=final.mp4 FileResponse;在 <pid>/、video/、voice/、characters/ 四处查找;防穿越(拒 /、\、..)

curl 示例(服务器本机,测试用免登 session 见 §8.1):

# 建项目
curl -s -X POST -H 'Content-Type: application/json' -H "Cookie: bxsid=$T" \
  -d '{"title":"测试剧","source_text":"少年在长安城追查画灵奇案…","style":"国漫"}' \
  http://127.0.0.1:8010/api/drama/projects

# 全链跑
curl -s -X POST -H 'Content-Type: application/json' -H "Cookie: bxsid=$T" \
  -d '{}' http://127.0.0.1:8010/api/drama/projects/1/run

# 只补跑 video 段(上游产物自动复用)
curl -s -X POST -H 'Content-Type: application/json' -H "Cookie: bxsid=$T" \
  -d '{"from_stage":"video","to_stage":"video"}' \
  http://127.0.0.1:8010/api/drama/projects/1/run

# 查状态 / 下载成片
curl -s -H "Cookie: bxsid=$T" http://127.0.0.1:8010/api/drama/projects/1
curl -s -H "Cookie: bxsid=$T" -o out.mp4 \
  'http://127.0.0.1:8010/api/drama/projects/1/file?name=final.mp4'

4.3 内部接口(媒体层,media_service)

video/voice/character 段复用的底座函数(签名以生产代码为准):

async def synth_lines(lines: list[dict], workdir: Path) -> list[dict]
    # lines=[{"text","role","emotion"}];全引擎降级:豆包seedtts→edge-tts→piper→静音
    # 返回 [{path, seconds(实测时长), ...}] 供 plan_timeline 排时间轴

def plan_timeline(items, total=30.0, endcard="") -> rows   # 配音排布 + 尾部字卡
def build_ass(rows, out_path: Path, title=...) -> Path     # 生成 ASS 字幕
def compose(rows, avatar: Path, out_mp4: Path, ..., total, ass, bg) -> None
    # ffmpeg 合成:画面(出画/静帧) + 配音轨 + 字幕 → final.mp4

def generate_bg_video(prompts: list[str], workdir: Path, total: float) -> Path
    # 逐镜出画 + 拼接;通道降级链:秘塔(METASO) → MiniMax → 可灵(KLING)

def generate_image(prompt, out_path: Path, size="1024x1536") -> None   # 定妆照
def image_generation_available() -> bool
def video_generation_available() -> bool

VOICES 音色表(media_service.VOICES):narrator / hexiangu / lvdongbin / yuan / xiaoyu 等键 → 不同 TTS 音色+风格;未知键回退 DEFAULT_ROLE。

4.4 LLM 网关接口

from ..agent import llm
res = await llm.complete(messages, tier="large"|"medium",
                         temperature=0.6, max_tokens=N,
                         offline_fallback='{"error":"...","data":{}}')
res.text, res.model   # 文本与实际命中模型名(落 drama_stage_runs.model)

供应商降级链(llm.PROVIDERS):deepseek → doubao(含 seed-2-1-lite)→ qwen(百炼)。tier=large 用旗舰档,medium 用快省档。


5. 技术选型

领域 选型 理由 / 备选
Web 框架 FastAPI + SQLModel + SQLite(WAL) 与矩阵总部 33 路由同栈,零新增依赖;建表 create_all、加列 _auto_add_columns 免迁移
LLM deepseek / doubao / qwen 三级降级(llm.py 网关) 单点 Key 故障自动切换;tier 分档控成本
技能集成 SKILL.md 全文注入 system prompt(skill_loader) 技能即提示词包,换技能=换文件不换代码;skill_hash 可溯源版本
TTS 豆包 seedtts → edge-tts → piper → 静音(四级降级) 免 Key 也能出声;synth_lines 实测时长回填时间轴
出图 generate_image(图库可用时,1024x1536 定妆照) 失败降级占位图,不阻塞产线
出画 秘塔(METASO) → MiniMax → 可灵 3.0(render_ai_clip 通道链) 均为按次计费通道,秘塔已充值为默认
视频合成 ffmpeg(compose:拼接+配音轨+ASS 字幕) 确定性、免费、可控;转场特效留给剪映后处理
JSON 解析 _parse_json_obj(容错 markdown 代码块/前后杂文) LLM 输出不保证纯净,必须容错
前端 原生 HTML/JS SPA(drama.html) 与 command/chat-panel 同风格,无构建链
部署 systemd dsp-hq.service(:8010) 已纳管;漫剧作为路由挂入,不单开进程

选型原则:复用 > 新建;确定性代码(合成/时间轴/落盘)与不确定性 LLM(剧本/分镜/质检)严格分界——LLM 只产 JSON,文件产物一律代码生成。


6. 部署方案

6.1 一键部署(可复现)

本地暂存目录 D:\八仙矩阵总部\_drama_deploy\,核心脚本 deploy_drama.py:

cd D:\八仙矩阵总部\_drama_deploy
python deploy_drama.py push      # 推送 20 个文件(代码+13 份 SKILL.md+drama.html)
python deploy_drama.py patch     # main.py 补丁:注册 drama_router + import tables(带时间戳备份)
python deploy_drama.py restart   # systemctl restart dsp-hq.service
python deploy_drama.py all       # 以上三步

推送机制:paramiko SSH + base64 传文件(SFTP 对 root 不可用);Windows 下传 /opt 路径必须加 MSYS_NO_PATHCONV=1(Git Bash 会把 /opt 转成 Windows 路径)。本地辅助工具:_test/ssh_run.py(执行远程命令)、_test/ssh_file.py(推文件)。

6.2 服务纳管

systemctl restart dsp-hq.service
systemctl is-active dsp-hq.service          # 期望 active
curl -s 127.0.0.1:8010/api/health           # 健康检查
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8010/static/drama.html  # 期望 200

日志:journalctl -u dsp-hq -o short-iso --since '10 min ago'(app.log 已弃用,一律走 journal)。

6.3 配置

零新增密钥——LLM/TTS/出画全复用矩阵总部既有通道(.env 既有配置)。换出画通道只改 .env 的 METASO/MINIMAX/KLING Key(video_generation_available() 自动探测)。产物根目录 ARTIFACT_ROOT = /opt/dsp-matrix-hq/data/drama(改路径动 pipeline.py 顶部常量)。

6.4 回滚

patch 前自动备份 main.py.bak-<时间戳>;回滚 = 恢复备份 + 重启。文件级回滚:用 git/暂存目录旧版重新 push。表结构只加不删,无需迁移回滚。


7. 开发进度计划

7.1 已完成(2026-10-31,v1.0 上线)

里程碑 内容 验证
M1 技能集成层 skill_loader + 13 份 SKILL.md 部署 + /api/drama/skills 台账 台账接口返回六段映射
M2 Agent 契约 agents.py 六节点指令+JSON 契约 E2E 各段 JSON 解析成功
M3 产线编排 pipeline 六段 + 复用/跳段/失败中止 + 双表留痕 项目#1 全链 done
M4 媒体链 voice TTS 四级降级 / video 出画+compose final.mp4 + 5 句 .mp3 落盘
M5 API+控制台 routers/drama.py + drama.html 页面 200,鉴权 401 正常
M6 E2E 验收 《长安画灵疑案》六段全绿 见 §8.2 实测记录
M7 运行可靠性加固 启动清扫(重启不留孤儿 running)+ 原子抢占防双跑 + 超时接管 + 段序校验 + prompt 快照/skill_hash 留痕 smoke_drama3.py 13/13 PASS
M8 成本护栏+自动化测试 video 重跑 force 确认 + run_count 上限 + pytest 回归套件 26 用例;顺带修截断抢救/写回判空/清扫双条件 pytest 26/26 PASS

7.2 迭代路线(v1.x,按优先级)

版本 目标 关键项 验收标准
v1.1 提质 ①剧本/分镜双稿择优(各跑 2 次按 qc 预检分选)②visual_anchor 全链强校验(缺锚定的镜头提示词自动补)③voice 情绪→TTS style 映射细化 主观评分:一致性问题率 ↓50%
v1.2 提效降本 ①medium 档下探(character/video 段换 lite 模型 A/B)②出画分辨率/时长分级(草稿 720p / 成片 1080p)③缓存同题材分镜复用 单集成本 ↓30%,时长 ↓30%
v1.3 多集能力 ①系列项目(parent_id + 角色圣经跨集继承)②批量跑 N 集 ③与 publish_tasks 打通一键入队(仍人工发) 3 集连跑角色一致性通过
v1.4 运营化 ①Eval 回归集(10 个固定题材回归跑)②bad case 标注回流 ③成本/耗时看板 每次改技能/模型自动回归

说明:以上为产品规划节奏(非项目排期承诺);每项上线前走冒烟清单(§8)。


8. 测试说明

8.1 免登冒烟手法(测试专用)

平台 API 需登录。测试可直接往 sessions 表插一行(created_at 必填 NOT NULL),cookie 名 bxsid:

TOKEN=smoke-$(date +%s)
EXP=$(date -u -d '+2 hours' '+%Y-%m-%d %H:%M:%S'); NOW=$(date -u '+%Y-%m-%d %H:%M:%S')
sqlite3 /opt/dsp-matrix-hq/data/hq.db \
  "INSERT OR REPLACE INTO sessions(token,user_id,tenant_id,expires_at,created_at) VALUES('$TOKEN',1,1,'$EXP','$NOW');"
# 用完清理:DELETE FROM sessions WHERE token LIKE 'smoke-%';

8.2 验收基准(v1.0 实测记录,项目#1《长安画灵疑案》)

段 实测结果 断言要点
script doubao 出稿 4 场 2 角,title/scene_id/dialogues 齐 output_json 可 json.loads;characters≤5
storyboard 4 镜 + visual_style 锚定 shots[].visual_prompt 60~120 字区间
character 2 张 AI 定妆照(阿七.png/画灵.png) portrait 路径存在或有 portrait_error
voice 5 句 TTS(d1~d5.mp3)实测时长 synth 结果含 path/seconds
video 4 段 Seedance 出画 + final.mp4(3.1MB+字幕) video_path 存在且可播
qc 90 分,命中「我靠」低俗词限流风险 + 整改建议 issues 非空、pass/score 合理

8.3 测试用例清单(回归用)

# 用例 步骤 期望
T1 全链冒烟 建项目→run 空参→轮询 status done,result_video 有路径
T2 跳段补跑 只跑 {"from_stage":"video","to_stage":"video"} 复用上游,不重跑 script
T3 上游缺失防御 新项目只跑 to_stage=video 该段 error(缺分镜),不抛 500,后续段不跑
T4 鉴权 无 Cookie 调任意端点 401
T5 租户隔离 B 租户 token 访问 A 的 pid 404
T6 文件穿越 ?name=../hq.db 400
T7 source_text 校验 空/超 20000 字 422
T8 幂等/并发 running 中重复 run 第二次拒绝或空转(不产生双产线)
T9 LLM 无 Key 清空 LLM Key 跑 script 段 error 含「LLM 不可用」,服务不崩
T10 无出画通道 清视频 Key 跑 video 产出配音+字幕版(video_error 说明缺通道),voice 版可出
T11 质检有效性 含低俗词的剧本跑 qc issues 命中并给 suggestion
T12 技能台账 GET /skills 六段映射与 STAGE_SKILLS 一致
T13 段序校验 run {"from_stage":"qc","to_stage":"script"} 400
T14 段名校验 run {"from_stage":"xxx"} 400
T15 运行防重 项目 running 且新鲜时再 run 409
T16 超时接管 置 running + updated_at 超 30 分钟再 run 200 允许接管
T17 启动清扫 造孤儿 running 项目/段记录后触发清扫(import 路由模块等价进程重启) 项目→error、段→error+「进程重启中断」
T18 可复现留痕 跑 script 段 prompts/script-*.txt 存在且含 skill_hash;input_json 含 skill_hash

T13~T18 由 smoke_drama3.py 覆盖(2026-10-31 实测 13/13 PASS,含 T0 建项目与收口断言)。

8.4 自动化回归套件(pytest,已上线)

# 服务器上运行(venv 已装 pytest 9.x):
/opt/dsp-matrix-hq/venv/bin/python -m pytest /opt/dsp-matrix-hq/tests/test_drama.py -v
# 跳过 LLM 调用(纯静态用例):SKIP_LLM=1 ...

26 个用例 = 单元层(JSON 截断抢救/技能包/契约/护栏真值表)+ 鉴权校验(T4~T7/T13/T14)+ 生命周期(T2/T3/T11/T18,含少量 LLM)+ 并发护栏(T15/T16/run_count 上限)+ 成本护栏(video 重跑 force)+ 清扫(T17/T17b 活跑不误杀)。默认不出画不花钱;全链出片(T1/T9/T10)为计费人工用例,不进自动化。2026-10-31 实测 26/26 PASS(3m49s)。

套件在测试中抓出并已修复的真 bug:①_parse_json_obj 截断抢救对「有键无值」尾巴失效+补括号嵌套顺序错(现按栈逆序补全);②bg 写回对已删行不判空(AttributeError 打崩后台任务);③清扫误杀活跑(现双条件:启动前遗留 且 行龄>120s);④重跑用例撞「新行未插入」竞态(wait 需 after_id 基线)。

8.5 本地静态检查(提交前必跑)

cd D:\八仙矩阵总部\_drama_deploy
python -m py_compile app/drama/*.py app/routers/drama.py   # Python 语法
# 注意:服务器 Python 3.10 —— f-string 内禁反斜杠表达式!本地 3.12 能过不代表服务器能过

9. 常见问题与排障(FAQ)

Q1. 服务起不来 / import 报错? 看 journalctl -u dsp-hq -o short-iso | tail -30。历史坑:① routers/drama.py 引用产线包必须 from ..drama import ...(相对导入两层,写成 .drama 会 ImportError);② f-string 里不能有反斜杠(Python 3.10)——正则类表达式提出来先赋值。

Q2. 某段卡住很久不出结果? 先查库:SELECT stage,status,datetime(started_at) FROM drama_stage_runs WHERE project_id=N ORDER BY id DESC LIMIT 3;。character/video 段含出图出画(单镜 20~60s,4 镜可到 3 分钟),属正常窗口,勿急着重启。journal 无异常 + 文件在增长 = 在跑。真卡死(>30 分钟无心跳)时再 run 会自动接管重跑。

Q2b. 服务重启后项目显示 running 但跑不动? 旧版本会永久卡死;现在启动清扫会自动把遗留 running 复位为 error(段记录标注「进程重启中断」),直接重新 run 即可。若仍见 running 且 updated_at 新鲜但确信已死,等 30 分钟超时接管或手工置 error。

Q3. 跳段跑报「缺少分镜产物」? 预期行为(T3):上游无 ok 产物时该段返回 error 不硬跑。先补跑上游段,或全链跑。

Q4. video 段有 video_error: 未配置视频生成通道? .env 缺 METASO/MINIMAX/KLING Key(video_generation_available() 为 False)。系统降级出「配音+字幕版」成片。配好任一通道 Key 重启即可。

Q5. 定妆照没出? image_generation_available() 为 False 或出图超时——character 段不阻塞,portrait_error 留痕,video 段自动用 placeholder.png 兜底。

Q6. LLM 输出解析失败 / 字段缺失? _parse_json_obj 已容错代码块与杂文;仍有缺字段段内 setdefault 兜底(title/scenes/shots/characters/lines/issues)。持续失败查 model 是否命中降级到离线 fallback(res.model 落在 stage_runs 里可查)。

Q7. 改了 SKILL.md 不生效? load_bundle 每次读盘,但运行中产线已把 system 消息发给 LLM——新项目/重跑该段才用新技能。若完全不生效,确认文件在 app/drama/skills/<stage>/ 且文件名为 SKILL.md(主)或 AUX_*.md(辅)。

Q8. 重跑某段会覆盖旧产物吗? DB 是追加式(每次新插 drama_stage_runs,取最近 ok);磁盘产物按段目录覆盖写。成片要看历史版本请先拷走。

Q9. SSH 部署时路径变成 C:/Program Files/Git/...? Git Bash 的 MSYS 路径转换。所有 ssh_run/ssh_file 调用前缀 MSYS_NO_PATHCONV=1。

Q10. 单集成本大概多少? LLM 六段约 2~6 万 token(deepseek/doubao 档);出画 4 镜走秘塔约 ¥3~5/集;TTS edge-tts 免费。合计 ¥5 以内/集(不含人工审片)。


10. 扩展指南

10.1 换/加技能(不改代码)

覆盖 skills/<stage>/SKILL.md 或新增 AUX_xxx.md → 重启(或下次跑段生效)。建议同步更新 skill_loader.STAGE_SKILLS 的展示名(台账)。

10.2 加一个新产线段(如「配乐」)

  1. skills/music/SKILL.md 放技能;
  2. agents.py 加 AGENTS["music"](instruction+output_spec);
  3. pipeline.py 加 _stage_music(ctx) 并注册 _STAGERS、插入 STAGE_ORDER(skill_loader);
  4. 上下游段从 ctx 取产物(用 .get 防御);
  5. 补 §8.3 用例。

10.3 换 LLM 档位/模型

只动 agents.py 的 tier/max_tokens;模型名单在 agent/llm.py::PROVIDERS,不建议产线直改。

10.4 接自动发布(谨慎)

原则「永不自动发布」。可做的最大步:video 段完成后把成片入 publish_tasks 队列(入队≠发布,发布仍人工确认)。接口对接见矩阵总部 publish_service。

10.5 对接变现(CPS 软广植入)

成片可关联 cps_links.drama_project_id(placement_type 承载植入方式),走 CPS 模块建挂载链接 + 分佣台账(见《漫剧商业变现与推广执行手册》第六章)。


附录 A · 关键文件索引

文件 作用 部署路径
skill_loader.py 技能加载(集成点) app/drama/
agents.py 6 Agent 契约 app/drama/
pipeline.py 六段编排 app/drama/
tables.py 两张表 app/drama/
routers/drama.py REST API app/routers/
drama.html 控制台 web/
deploy_drama.py 部署脚本 本地 _drama_deploy/
smoke_drama.py / smoke_drama2.py 冒烟脚本 本地 _drama_deploy/

附录 B · 术语