topfans/docs/specs/2026-06-02-dify-laser-card-workflow.md
2026-06-03 22:19:22 +08:00

12 KiB
Raw Blame History

Dify 工作流配置文档:laser_card_variants_v1

文件路径: docs/specs/2026-06-02-dify-laser-card-workflow.md

本文档是 Dify 平台上 laser_card_variants_v1 工作流的完整配置指南。

工作流概述

  • 类型: WorkflowAPI 触发)
  • 名称: laser_card_variants_v1
  • 用途: 接收用户图片 → 抠图 → MiniMax AI 生成背景+装饰 → laser-compositor 合成 → 返回 5 张镭射卡 URL
  • 预计耗时: 30-60 秒

开始节点 — 输入变量

Dify 平台限制: Dify 的开始节点不支持 array 类型。preset_codesrender_configs 使用 文本 (string) 类型,传入 JSON 字符串,在代码节点中 json.loads 解析。

变量 Dify 类型 必填 默认值 说明
source_image_url 文本 (string) 用户原图 OSS signed URL
use_cutout 复选框 (boolean) 是否人像抠图
preset_codes 文本 (string) ["dream","classic","holoFull","ice","sunset"] JSON 数组字符串,例如 "[\"dream\",\"classic\"]"
render_configs 文本 (string) JSON 数组字符串,包含每个 variant 的 grating_config、bg_prompt、overlay_prompt

Dify 平台操作步骤:

  1. 添加 preset_codes:类型 文本,变量名 preset_codes勾选必填,默认值填 ["dream","classic","holoFull","ice","sunset"]
  2. 添加 render_configs:类型 文本,变量名 render_configs勾选必填,默认值留空
  3. 确认 source_image_url(文本,必填)和 use_cutout(复选框,必填)正确
  4. 保存开始节点

节点链路

flowchart TB
    START[开始节点] --> NORM[代码节点1: 参数展开]
    NORM --> BRANCH{条件: use_cutout?}
    BRANCH -->|true| SEG[HTTP节点: Gateway /segment 抠图]
    BRANCH -->|false| LOOP
    SEG -->|成功| LOOP[循环节点: preset_codes x5]
    SEG -->|失败| WARN1[添加 warning]
    WARN1 --> LOOP

    subgraph variant_loop[循环内: 每个 variant]
        BG[HTTP节点: MiniMax 生成背景图]
        DECO[HTTP节点: MiniMax 生成装饰图]
        COMP[HTTP节点: laser-compositor /compose]
        BG --> DECO --> COMP
    end

    LOOP --> variant_loop
    COMP --> AGG[代码节点2: 聚合输出]
    AGG --> END_NODE[结束节点]

各节点详细配置

节点 1代码节点 — 参数展开

输入: preset_codes, render_configs, use_cutout

代码Python

import json

def main(preset_codes: str, render_configs: str, use_cutout: bool) -> dict:
    """
    解析 JSON 字符串并展开 variants 列表
    因为 Dify 开始节点不支持 array 类型preset_codes 和 render_configs 都以 string 传入
    """
    # 解析 preset_codes字符串 → 列表)
    if preset_codes and preset_codes.strip():
        try:
            preset_list = json.loads(preset_codes)
        except:
            preset_list = []
    else:
        preset_list = []

    if not preset_list:
        preset_list = ["dream", "classic", "holoFull", "ice", "sunset"]

    # 解析 render_configs字符串 → 列表)
    if render_configs and render_configs.strip():
        try:
            configs = json.loads(render_configs)
        except:
            configs = []
    else:
        configs = []

    # 构建 preset_id → config 映射
    config_map = {rc.get("preset_id"): rc for rc in configs if "preset_id" in rc}

    variants = []
    for pc in preset_list:
        if pc in config_map:
            cfg = config_map[pc]
            variants.append({
                "preset_id": pc,
                "grating_config": cfg.get("grating_config", {}),
                "bg_prompt": cfg.get("bg_prompt", ""),
                "overlay_prompt": cfg.get("overlay_prompt", ""),
            })

    return {
        "variants": json.dumps(variants),
        "variant_count": len(variants),
        "use_cutout": use_cutout,
    }

节点 2条件分支 — 抠图判断

条件: {{#代码节点1.use_cutout#}} == true

  • true 分支 → HTTP 节点调用抠图 API
  • false 分支 → 直接进入循环

节点 3HTTP 节点 — 抠图Segment

方法: POST
URL: http://{GATEWAY_HOST}/api/v1/segment
Headers:
  Content-Type: multipart/form-data
Body (form-data):
  image: {{文件上传引用}}
  scene: portrait
Authorization: Bearer {{#env.GATEWAY_JWT_TOKEN#}}

注意: Dify HTTP 节点的 multipart 能力有限。如果 Dify 不支持 multipart form-data可以用代码节点构造 HTTP 请求,或改为让 Gateway 提供 JSON 接口(传入 image_url

替代方案推荐Gateway 扩展 /api/v1/segment 支持 JSON body

POST http://{GATEWAY_HOST}/api/v1/segment/json
Body (JSON):
{
  "image_url": "{{#开始节点.source_image_url#}}",
  "scene": "portrait"
}

返回:

{
  "success": true,
  "cutout_oss_key": "laser-card/.../xxx_cutout.png",
  "cutout_url_signed": "https://oss.example.com/..."
}

如果失败:设置 cutout_url = "",记 warning。


节点 4循环节点 — variant 遍历

循环对象: {{#代码节点1.variants#}}JSON array 循环变量名: variant_item

循环内包含 3 个子节点(串行执行):


子节点 4aMiniMax 生成背景图

方法: POST
URL: https://api.minimaxi.com/v1/image_generation
Headers:
  Authorization: Bearer {{#env.MINIMAX_API_KEY#}}
  Content-Type: application/json
Body (JSON):
{
  "model": "image-01",
  "prompt": "{{#variant_item.bg_prompt#}}",
  "aspect_ratio": "3:4",
  "n": 1,
  "prompt_optimizer": true,
  "response_format": "url"
}

超时: 30 秒

输出提取:

  • bg_url = {{#输出.body.data[0].url#}}

如果失败,设置 bg_url = "",记 warning。


子节点 4bMiniMax 生成装饰图

方法: POST
URL: https://api.minimaxi.com/v1/image_generation
Headers:
  Authorization: Bearer {{#env.MINIMAX_API_KEY#}}
  Content-Type: application/json
Body (JSON):
{
  "model": "image-01",
  "prompt": "{{#variant_item.overlay_prompt#}}",
  "aspect_ratio": "3:4",
  "n": 1,
  "prompt_optimizer": true,
  "response_format": "url"
}

超时: 30 秒

输出提取:

  • overlay_url = {{#输出.body.data[0].url#}}

子节点 4claser-compositor 合成

方法: POST
URL: http://{LASER_COMPOSITOR_HOST}/compose
Headers:
  Content-Type: application/json
Body (JSON):
{
  "background_url": "{{#子节点4a.bg_url#}}",
  "cutout_url": "{{#代码节点3.cutout_url_signed#}}",
  "overlay_url": "{{#子节点4b.overlay_url#}}",
  "grating_config": {{#variant_item.grating_config#}},
  "export_width": 450,
  "export_height": 600,
  "variant_index": {{#循环索引#}},
  "output_oss_key": "laser-card/{date}/{uuid}_variant_{{循环索引}}"
}

超时: 30 秒

返回:

{
  "status": "succeeded",
  "variant_index": 0,
  "width": 450,
  "height": 600,
  "oss_key": "laser-card/.../variant_0.png",
  "signed_url": "https://oss.example.com/..."
}

节点 5代码节点 — 聚合输出

目的: 将循环中 5 个 variant 的结果聚合为统一 JSON

import json

def main(variant_results: list, cutout_url: str, warnings: list) -> dict:
    """
    聚合循环结果
    variant_results: 从循环节点收集的结果数组
    """
    variants = []
    output_warnings = warnings.copy() if warnings else []

    for i, vr in enumerate(variant_results):
        if vr and vr.get("status") == "succeeded":
            variants.append({
                "preset_id": vr.get("preset_id", f"variant_{i}"),
                "oss_key": vr.get("oss_key", ""),
                "signed_url": vr.get("signed_url", ""),
                "width": vr.get("width", 450),
                "height": vr.get("height", 600),
            })
        else:
            output_warnings.append(f"variant_{i} failed")

    return {
        "status": "succeeded" if len(variants) > 0 else "failed",
        "variants": variants,
        "cutout_oss_key": cutout_url or "",
        "warnings": output_warnings,
    }

结束节点 — 输出格式

{
  "status": "succeeded",
  "variants": [
    {
      "preset_id": "dream",
      "oss_key": "laser-card/xxxx/variant_0.png",
      "signed_url": "https://oss.example.com/...",
      "width": 450,
      "height": 600
    }
  ],
  "cutout_oss_key": "laser-card/xxxx/cutout.png",
  "warnings": []
}

环境变量(在 Dify 工作流中配置)

变量 说明
GATEWAY_HOST Gateway 地址,如 http://192.168.1.100:8080
MINIMAX_API_KEY MiniMax API 密钥
LASER_COMPOSITOR_HOST laser-compositor 地址,如 http://127.0.0.1:7000

鉴权说明

Dify 工作流调用 Gateway /api/v1/segment(需要 JWT需要在 Dify 的 HTTP 节点中传入 Bearer Token。有两种方式

  1. 在 Dify 环境变量中配置一个长期有效的 JWT tokenGATEWAY_JWT_TOKEN)— 简单但需要定期维护
  2. Gateway 扩展 /api/v1/segment/json — 不强制 JWT 鉴权仅限内网调用Dify→Gateway 走内网)

推荐方案 2减少 Token 维护成本。


装饰图 prompt 模板

每个 preset 的 bg_promptoverlay_prompt 由前端 laserPresets.jsbuildRenderConfigs() 动态生成并传入。Dify 不需要维护 prompt 数据。

5 个 preset 的参考 prompt维护在前端 laserPresets.js

Preset bg_prompt overlay_prompt
dream 梦幻柔光渐变背景淡紫色调和粉色柔和泡泡和星光光斑虚化景深效果3:4竖版比例 精致银色星点散布细边框装饰线透明背景SVG风格手绘装饰元素
classic 经典复古胶片风格背景暖色调浅香槟色渐变艺术纹理和柔和光影3:4竖版比例 经典复古边框花纹装饰,暖金色调和银色细线,透明背景,精致几何图案
holoFull 全息科技感背景银色金属质感未来主义光影和几何线条珍珠光泽渐变3:4竖版比例 全息科技感装饰边框,霓虹色细线光晕,透明背景,电路板几何图案和星形闪光
ice 冰雪冷色调背景浅蓝灰渐变冰晶纹理和雪花光斑清爽透明感3:4竖版比例 冰晶装饰边框,浅蓝色和银色星点,透明背景,雪花图案和细线装饰
sunset 日落暖色调背景玫瑰金渐变暖橙色和粉色光影温馨氛围3:4竖版比例 日落暖色调装饰边框,玫瑰金和琥珀色星点,透明背景,柔和花卉和光线元素

测试方法

  1. 在 Dify 平台创建 Workflow按本文档配置各节点特别注意preset_codesrender_configs文本类型)
  2. 使用 Dify 的"运行"功能测试,传入示例 input

关键: preset_codesrender_configs 的值必须是 JSON 字符串(带转义引号),不是 JSON 对象。

{
  "source_image_url": "https://oss.example.com/test/user_photo.jpg",
  "use_cutout": true,
  "preset_codes": "[\"dream\"]",
  "render_configs": "[{\"preset_id\":\"dream\",\"grating_config\":{\"sheen_band_angle\":135,\"sheen_intensity\":0.40,\"sheen_speed\":0.35,\"foil_coverage\":0.75,\"backdrop_tone\":\"#A8ACB2\"},\"bg_prompt\":\"梦幻柔光渐变背景,淡紫色调\",\"overlay_prompt\":\"精致银色星点装饰,透明背景\"}]"
}

外部 API 调用示例(通过 Gateway 触发):

{
  "source_image_url": "https://oss.example.com/photo.jpg",
  "use_cutout": true,
  "preset_codes": "[\"dream\",\"classic\"]",
  "render_configs": "[{\"preset_id\":\"dream\",\"grating_config\":{\"sheen_band_angle\":135,\"sheen_intensity\":0.4},\"bg_prompt\":\"...\",\"overlay_prompt\":\"...\"}]"
}
  1. 单个 preset 测试成功后,改为 5 个 preset 全量测试