Skip to content

Storyboard 基础设施排雷与 AIGC 探索方案

> 文档维护者: 全栈后端架构师 & AI 产品经理 > 更新日期: 2026-03-25 > 核心任务: 基础设施稳定化 + AIGC 内容脑暴


🎯 任务 A:硬核排雷 (Infrastructure Stabilization)

1. 链路排查方案:10分钟无响应精准定位指南

当视频提取任务在前端显示"处理中"长达 10 分钟没反应时,按照以下步骤精准定位:

📊 排查流程图

前端"处理中" (10分钟)

[步骤 1] 数据库状态验证
    ↓ (process_status = 1)
[步骤 2] PHP 端日志检查
    ↓ (有任务触发记录)
[步骤 3] Celery 队列状态检查
    ↓ (任务在队列中)
[步骤 4] Worker 日志与状态检查
    ↓ (Worker 正在处理)
[步骤 5] Python 任务日志深度排查

定位到具体卡死环节

🔍 详细排查步骤

步骤 1:数据库状态验证 (最快)

检查点account_direction_reference_video

sql
-- 查看该任务的当前状态
SELECT 
    id, 
    title, 
    process_status, 
    create_time, 
    update_time,
    TIMESTAMPDIFF(MINUTE, create_time, NOW()) as minutes_elapsed
FROM account_direction_reference_video 
WHERE id = {inspiration_id};

-- 查看最近 10 分钟所有处理中的任务
SELECT 
    id, 
    title, 
    process_status, 
    create_time,
    TIMESTAMPDIFF(MINUTE, create_time, NOW()) as minutes_elapsed
FROM account_direction_reference_video 
WHERE process_status = 1 
  AND create_time > DATE_SUB(NOW(), INTERVAL 10 MINUTE)
ORDER BY create_time DESC;

process_status 状态码说明

  • 0 = 未处理
  • 1 = 处理中 (🔴 如果卡在这里超过 10 分钟)
  • 2 = 已完成
  • 3 = 失败

判断依据

  • 如果 process_status != 1 → 不是卡在链路中,状态已更新
  • 如果 update_time 很久没变 → 任务确实卡死了

步骤 2:PHP 端日志检查

日志路径api/runtime/log/

bash
# 进入 api 目录
cd /Users/mzy/docker/dnmp/www/stooland/api

# 查看今天的日志
tail -f runtime/log/$(date +%Y%m%d).log

# 搜索特定 inspiration_id 的日志
grep "inspiration_id\|PyBridge" runtime/log/$(date +%Y%m%d).log

# 搜索 Webhook 回调记录
grep "video-asset-extracted" runtime/log/$(date +%Y%m%d).log

关键日志关键字

  • Triggered PyBridge video asset processing → PHP 成功触发任务
  • Failed to trigger PyBridge → 触发失败
  • Webhookvideo-asset-extracted → 收到回调

判断依据

  • Triggered 但无 Webhook → 问题在 Python/Celery 端
  • Triggered → 问题在 PHP 端触发环节

步骤 3:Celery 队列状态检查

检查命令

bash
# 进入 muse-engine 目录
cd /Users/mzy/docker/dnmp/www/stooland/muse-engine

# 方式 1: 使用 celery inspect 查看队列状态
celery -A src.celery_app inspect active

# 查看已注册的任务
celery -A src.celery_app inspect registered

# 查看队列长度 (需要 flower 或直接连 Redis)
redis-cli -h localhost -p 6379 LLEN celery

# 如果配置了 flower,浏览器打开监控面板
# http://localhost:5555

判断依据

  • inspect active 为空 → 任务没进队列,或队列积压
  • LLEN celery 数值很大 → 队列积压严重

步骤 4:Worker 日志与状态检查

Worker 日志位置:根据启动方式而定

bash
# 如果是用 start.sh 启动的,查看日志文件
cd /Users/mzy/docker/dnmp/www/stooland/muse-engine
tail -f nohup.out 2>&1 | grep -E "process_video_assets|ERROR|CRITICAL"

# 或者查看系统进程
ps aux | grep celery

# 检查 Worker 是否存活
celery -A src.celery_app inspect ping

关键日志模式

# 正常启动
[INFO] celery@xxx ready.

# 任务接收
[INFO] Received task: src.tasks.video_tasks.process_video_assets[xxx]

# 任务进度
[job_id] Starting video asset processing...
[job_id] Downloading video...
[job_id] Extracting audio...
[job_id] Extracting frames...

# 异常
[ERROR] CRITICAL ERROR: ...
[WARNING] Anti-scraping protection triggered

判断依据

  • Received task 但没有后续进度 → 任务在下载或处理环节卡死
  • Anti-scraping → 被反爬拦截

步骤 5:Python 任务日志深度排查

Job 日志位置muse-engine/tmp/ 或配置的日志目录

bash
# 查看具体 job_id 的日志
# 首先从数据库或 PHP 日志找到 job_id

# 如果有 job_id,直接查找相关日志
cd /Users/mzy/docker/dnmp/www/stooland/muse-engine
find . -name "*{job_id}*" -type f 2>/dev/null

# 查看 tmp 目录下最近的日志
ls -lt tmp/ | head -20

# 检查临时文件是否残留
ls -lt tmp/dl_*.mp4 2>/dev/null | head -10
ls -lt tmp/audio_*.mp3 2>/dev/null | head -10

卡死环节定位

最后看到的日志卡死环节可能原因
Downloading video to...视频下载1. 网络慢/超时<br>2. 被反爬返回 HTML<br>3. 链接失效
Extracting audio...音频提取1. 视频文件损坏<br>2. FFmpeg 卡死
Extracting frames...帧提取1. 视频码率异常<br>2. 内存不足
Uploading audio to OSSOSS 上传1. OSS 连接问题<br>2. 权限问题
Sending callback to...回调 PHP1. PHP 服务不可达<br>2. 网络超时

🛠️ 快速排查脚本 (一键诊断)

创建快速诊断脚本:

bash
#!/bin/bash
# 文件名: diagnose_stuck_task.sh
# 使用: ./diagnose_stuck_task.sh &lt;inspiration_id&gt;

INSPIRATION_ID=$1
if [ -z "$INSPIRATION_ID" ]; then
    echo "Usage: $0 &lt;inspiration_id&gt;"
    exit 1
fi

echo "========================================"
echo "🔍 Task Diagnostics for ID: $INSPIRATION_ID"
echo "========================================"

# 步骤 1: 数据库状态
echo ""
echo "[1/5] 检查数据库状态..."
# (需要配置数据库连接)

# 步骤 2: PHP 日志
echo ""
echo "[2/5] 检查 PHP 日志..."
cd /Users/mzy/docker/dnmp/www/stooland/api
grep -C 5 "inspiration_id.*$INSPIRATION_ID" runtime/log/$(date +%Y%m%d).log || echo "未找到该 ID 的 PHP 日志"

# 步骤 3: Celery 队列
echo ""
echo "[3/5] 检查 Celery 队列..."
cd /Users/mzy/docker/dnmp/www/stooland/muse-engine
celery -A src.celery_app inspect active 2&gt;/dev/null || echo "Celery inspect 失败,请检查 Worker"

# 步骤 4: Worker 日志
echo ""
echo "[4/5] 检查 Worker 最新日志..."
if [ -f nohup.out ]; then
    tail -50 nohup.out | grep -E "process_video_assets|ERROR|CRITICAL|WARNING"
else
    echo "未找到 nohup.out,请检查 Worker 启动方式"
fi

echo ""
echo "========================================"
echo "✅ 诊断完成"
echo "========================================"

2. Playwright Fallback 策略工程化集成方案

📐 架构设计

┌─────────────────────────────────────────────────────────┐
│                    PHP VideoExtractService               │
├─────────────────────────────────────────────────────────┤
│  通道 0-7 (第三方 API, 轻量级, 快速)                    │
│  parseWithHhlqilongzhu() → parseWithYujn() → ...       │
└──────────────────────┬──────────────────────────────────┘
                       │ 全部失败

┌─────────────────────────────────────────────────────────┐
│         新增通道 8: Playwright Fallback Gateway         │
├─────────────────────────────────────────────────────────┤
│  调用 Python HTTP 服务 /api/playwright/extract         │
└──────────────────────┬──────────────────────────────────┘


┌─────────────────────────────────────────────────────────┐
│           Python: Playwright Extraction Service         │
├─────────────────────────────────────────────────────────┤
│  PlaywrightFallbackStrategy.execute()                   │
│  → 无头浏览器访问                                         │
│  → 拦截 Network .mp4 流                                  │
│  → 返回 video_url                                        │
└──────────────────────┬──────────────────────────────────┘


              返回给 PHP,继续后续流程

🔧 实施步骤

步骤 1: 在 muse-engine 中创建 Playwright HTTP 服务

创建文件:muse-engine/src/service/playwright_service.py

python
# -*- coding: utf-8 -*-
import asyncio
import sys
import os
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from loguru import logger

# 添加 social-scraper 到路径
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'social-scraper'))

from strategies.playwright_fallback_strategy import PlaywrightFallbackStrategy

router = APIRouter(prefix="/playwright", tags=["playwright"])

class PlaywrightExtractRequest(BaseModel):
    url: str
    headless: bool = True

class PlaywrightExtractResponse(BaseModel):
    status: str
    video_url: str = None
    title: str = None
    message: str = None

@router.post("/extract", response_model=PlaywrightExtractResponse)
async def extract_video(request: PlaywrightExtractRequest):
    """
    使用 Playwright 兜底策略提取视频
    """
    logger.info(f"[PlaywrightService] 收到提取请求: {request.url[:80]}...")
    
    try:
        strategy = PlaywrightFallbackStrategy(headless=request.headless)
        result = await strategy.execute(request.url)
        
        logger.info(f"[PlaywrightService] 提取结果: {result}")
        
        return PlaywrightExtractResponse(
            status=result.get("status"),
            video_url=result.get("video_url"),
            title=result.get("title"),
            message=result.get("message")
        )
        
    except Exception as e:
        logger.error(f"[PlaywrightService] 提取异常: {e}")
        raise HTTPException(status_code=500, detail=str(e))

步骤 2: 在 fastapi_app.py 中注册路由

修改 muse-engine/src/service/fastapi_app.py,添加:

python
# 在文件顶部导入
from src.service.playwright_service import router as playwright_router

# 在 create_fastapi_app() 函数中添加
app.include_router(playwright_router)

步骤 3: 在 PHP VideoExtractService 中添加通道 8

修改 api/app/aigc/service/VideoExtractService.php,添加新方法:

php
/**
 * 通道 8: Playwright 兜底策略 (最终武器)
 */
protected function parseWithPlaywright(string $url): ?array
{
    try {
        Log::info("[VideoExtractService] 启用 Playwright 兜底策略: " . $url);
        
        $pybridgeUrl = env('APP.PYBRIDGE_URL', 'http://127.0.0.1:8787');
        
        $response = $this-&gt;client-&gt;post($pybridgeUrl . '/playwright/extract', [
            'json' =&gt; [
                'url' =&gt; $url,
                'headless' =&gt; true
            ],
            'timeout' =&gt; 45.0 // Playwright 需要更长时间
        ]);

        $body = $response-&gt;getBody()-&gt;getContents();
        $data = json_decode($body, true);

        if (isset($data['status']) &amp;&amp; $data['status'] === 'success') {
            $videoUrl = trim($data['video_url'] ?? '', " `");
            
            if (!empty($videoUrl)) {
                return [
                    'video_url' =&gt; $videoUrl,
                    'cover_url' =&gt; '',
                    'title' =&gt; $data['title'] ?? '未命名视频',
                    'platform' =&gt; 'douyin',
                    'source_url' =&gt; $url
                ];
            }
        } elseif (isset($data['status']) &amp;&amp; $data['status'] === 'not_found') {
            Log::warning("[VideoExtractService] Playwright 检测到作品已删除: " . $data['message']);
            return null;
        }
        
        Log::warning("[VideoExtractService] Playwright 提取失败: " . ($data['message'] ?? 'unknown'));
        return null;
        
    } catch (\Exception $e) {
        Log::error("[VideoExtractService] Playwright exception: " . $e-&gt;getMessage());
        return null;
    }
}

然后在 extract() 方法的通道列表末尾添加:

php
// 通道 8: Playwright 兜底策略 (最终武器)
Log::info("[VideoExtractService] All APIs failed, trying Playwright fallback...");
$result = $this-&gt;parseWithPlaywright($url);
if ($result) {
    return $result;
}

步骤 4: 配置依赖与环境

muse-engine/requirements.txt 中添加:

playwright>=1.40.0

安装 Playwright 浏览器:

bash
cd /Users/mzy/docker/dnmp/www/stooland/muse-engine
pip install playwright
playwright install chromium

步骤 5: 增加超时与重试机制

muse-engine/src/tasks/video_tasks.pyprocess_video_assets_task 中:

python
# 下载视频时增加更完善的重试
async def download():
    headers = {"User-Agent": "Mozilla/5.0 ..."}
    client_kwargs = {
        "timeout": 300.0,  # 5 分钟
        "follow_redirects": True,
        "headers": headers,
        "trust_env": True
    }
    
    async with httpx.AsyncClient(**client_kwargs) as client:
        max_retries = 5  # 增加到 5 次
        backoff_times = [2, 5, 10, 20, 30]  # 指数退避
        
        for attempt in range(max_retries):
            try:
                # ... 原有下载逻辑 ...
                
                if success:
                    return True
                    
            except Exception as e:
                last_error = e
                logger.warning(f"Download attempt {attempt + 1} failed: {str(e)}")
                
                if attempt &lt; max_retries - 1:
                    wait_time = backoff_times[attempt]
                    logger.info(f"Waiting {wait_time}s before retry...")
                    await asyncio.sleep(wait_time)
        
        raise Exception(f"Failed after {max_retries} attempts")

📊 监控与告警

建立 Playwright 使用监控:

python
# 在 playwright_service.py 中添加统计
from datetime import datetime

playwright_stats = {
    "total_calls": 0,
    "success_count": 0,
    "failed_count": 0,
    "not_found_count": 0,
    "last_calls": []
}

@router.post("/extract")
async def extract_video(request: PlaywrightExtractRequest):
    global playwright_stats
    playwright_stats["total_calls"] += 1
    
    # ... 原有逻辑 ...
    
    if result.get("status") == "success":
        playwright_stats["success_count"] += 1
    elif result.get("status") == "not_found":
        playwright_stats["not_found_count"] += 1
    else:
        playwright_stats["failed_count"] += 1
    
    # 记录最近 100 次调用
    playwright_stats["last_calls"].append({
        "time": datetime.now().isoformat(),
        "url": request.url[:50],
        "status": result.get("status")
    })
    if len(playwright_stats["last_calls"]) &gt; 100:
        playwright_stats["last_calls"].pop(0)

@router.get("/stats")
async def get_stats():
    return playwright_stats

🎯 任务 B:AIGC 奇观脑暴 (Product Brainstorming)

水果 + AIGC 趣味内容短视频策划

基于现有能力(火山 ASR、ComfyUI/即梦图像生成、TTS),针对"水果行不行"账号转型,提出以下 3 个具体短视频策划方向:


🎬 策划方向一:《水果赛博朋克图鉴》

核心概念:用 ComfyUI 将普通水果变成赛博朋克风格的"未来水果",打造视觉奇观

内容范式

[0-3秒] 钩子开场 (视觉冲击)
  → 画面:暗黑背景,霓虹灯光闪烁,一个普通苹果缓缓浮现
  → 台词(TTS):"你见过 2077 年的苹果吗?"
  → 音效:电子脉冲声 + glitch 特效

[3-15秒] AI 变身过程 (奇观展示)
  → 画面:普通苹果 → (分镜切换/渐变) → 赛博朋克苹果
    - 表皮变成金属质感 + 霓虹纹路
    - 果柄变成光纤/数据线
    - 周围漂浮 hologram 文字:"CYBER-FRUIT v2.0"
  → 台词(TTS):"注入赛博基因,启动霓虹涂层... 完成!"
  → BGM:赛博朋克风电子乐

[15-25秒] 系列展示 (完播钩子)
  → 画面:快速切换 3-5 种赛博水果
    - 赛博西瓜:外壳是透明玻璃罩,内部果肉是发光像素
    - 赛博草莓:表面是 LED 点阵,会变换颜色
    - 赛博榴莲:尖刺变成能量武器造型
  → 台词(TTS):"西瓜、草莓、榴莲... 每一个都是未来限定!"

[25-30秒] 互动结尾
  → 画面:所有赛博水果排成一排 + 文字叠加
  → 台词(TTS):"下一个你想看什么水果赛博化?评论区告诉我!"
  → 画面文字:"点赞关注,解锁更多未来水果!"

技术实现路径

  1. 即梦/ComfyUI 工作流
    • ControlNet + Reference Only 保持水果轮廓
    • LoRA:cyberpunk_style_v1 + neon_lights_v2
    • Prompt:cyberpunk style, glowing neon lines, metallic texture, futuristic, {fruit}, holographic interface, dark background, 8k, ultra detailed
  2. 分镜脚本生成:让 LLM 基于这个范式自动生成分镜
  3. 后期合成:用 FFmpeg 做转场、加 Glitch 特效、BGM

🎬 策划方向二:《水果冷笑话 AI 剧场》

核心概念:水果拟人化,用数字人讲水果冷笑话,搞笑 + 轻科普

内容范式

[0-2秒] 快速开场
  → 画面:数字人(水果造型)出现
  → 台词(TTS):"嘿,我是小苹果!"

[2-12秒] 冷笑话表演
  → 画面:数字人做夸张表情 + 手势
  → 台词(TTS):
    "你知道为什么苹果最怕去医院吗?"
    (停顿 2 秒)
    "因为医生总是说:'来,把苹果核(hu)交出来!'"
  → 音效:"咚~"(冷场鼓声)+ 乌鸦飞过

[12-20秒] 趣味科普 (价值点)
  → 画面:苹果切开的特写 + 知识点字幕
  → 台词(TTS):
    "不过说真的,苹果核其实含有微量氰化物,
    但只要不吃个几十斤,根本没事~"
  → 画面文字:"知识冷,但有用!"

[20-28秒] 互动 + 预告
  → 画面:数字人 wink + 下集预告
  → 台词(TTS):
    "明天听我讲榴莲的社死现场!
    点赞关注,水果笑话天天见~"

技术实现路径

  1. 数字人:阿里数字人或即梦数字人,定制水果主题 avatar
  2. TTS:火山引擎 TTS,用活泼/搞笑音色
  3. 冷笑话生成:LLM Prompt:你是一个水果冷笑话专家。请生成关于{水果}的冷笑话,100字以内,要谐音梗。然后再补充一个关于该水果的趣味小知识。
  4. 自动流水线:可以做成完全自动化的日更内容

🎬 策划方向三:《水果时光倒流机》

核心概念:用 AI 让水果"逆生长",从成品水果变回树苗、种子,视觉奇观 + 治愈感

内容范式

[0-3秒] 钩子提问
  → 画面:一个鲜红的草莓特写
  → 台词(TTS):"你见过草莓变回种子的样子吗?"
  → BGM:神秘、好奇的氛围音乐

[3-20秒] 逆生长奇观 (核心看点)
  → 画面:用 AI 生成的"逆生长"动画序列
    - 阶段 1 (0-5s):成熟草莓 → 半生草莓(颜色变淡,体积缩小)
    - 阶段 2 (5-10s):半生草莓 → 小白花(花瓣慢慢张开)
    - 阶段 3 (10-15s):小白花 → 草莓苗(叶子缩回,茎变细)
    - 阶段 4 (15-20s):草莓苗 → 种子(最终回到一颗小小的种子)
  → 台词(TTS):
    "让时光倒流... 3... 2... 1...
    从果实,到花朵,到青苗,最后回到最初的那颗种子。"
  → 特效:画面加上"倒带"滤镜、时间轴数字倒流

[20-28秒] 治愈升华
  → 画面:种子落在土壤里,渐渐发芽(快速正向播放)
  → 台词(TTS):
    "但每一颗种子,都蕴含着再次变成果实的力量。
    就像你我,随时可以重新开始。"
  → BGM:温暖、治愈的音乐

[28-30秒] 结尾互动
  → 画面:文字叠加
  → 台词(TTS):"明天想看什么水果的时光倒流?"
  → 画面文字:"点赞 + 关注,治愈每天"

技术实现路径

  1. 关键帧生成
    • 用即梦/ComfyUI 生成 5-8 个关键帧(成熟、半熟、开花、幼苗、种子...)
    • Prompt:{stage description} of {fruit}, hyper realistic, natural lighting, 8k
  2. 帧插值:用 RIFE 或 FILM 做帧间插值,生成平滑过渡
  3. 视频合成:FFmpeg 把图片序列转视频,加 BGM、字幕、特效
  4. 批量生产:可以做一个"水果选择器",选水果自动生成全套

📊 三个策划对比

策划方向核心卖点技术难度量产性目标受众
赛博朋克图鉴视觉奇观、酷炫⭐⭐⭐⭐⭐科技爱好者、Z世代
冷笑话 AI 剧场搞笑、日更轻松⭐⭐⭐⭐⭐泛娱乐大众、学生
时光倒流机治愈、反差感⭐⭐⭐⭐⭐⭐喜欢治愈内容的用户

🎯 总结与行动计划

基础设施排雷优先级

  1. 立即执行:按照"链路排查方案"建立诊断流程
  2. 本周完成:Playwright Fallback 工程化集成
  3. 本周内:增加超时监控与自动重试机制

AIGC 内容探索优先级

  1. 先试水:《水果冷笑话 AI 剧场》(技术门槛最低,容易量产)
  2. 同步探索:《水果赛博朋克图鉴》(视觉效果最好,容易出爆款)
  3. 长期规划:《水果时光倒流机》(技术最复杂,但差异化最强)