1. 项目概述当 ComfyUI 不再是“点一下出一张图”的玩具“我用 WorkBuddy 把本地 ComfyUI 管成了一条出图流水线”——这句话刚在技术群刷出来时我盯着看了三秒。不是因为听不懂而是太懂了ComfyUI 本身是个节点式图像生成工作流引擎但默认状态下它更像一个高级画板——你拖拽节点、连好线、填好参数、点一次“Queue Prompt”出一张图再想出下一张得手动改参数、重连线、再点一次。重复十次手酸眼花重复一百次心态崩盘。而“流水线”三个字直击所有批量出图场景的命门电商主图批量换背景、IP形象多角度渲染、AIGC内容工厂级交付、甚至只是给团队成员统一生成风格化头像……这些都不是“单张图能解决的事”。WorkBuddy 这个名字听起来像协作工具但它在 ComfyUI 生态里干的其实是“工业调度员”的活它不碰模型权重不改采样器逻辑也不重写 VAE 解码过程而是把原本散落在 UI 界面、JSON 配置、命令行参数里的“执行意图”抽象成可定义、可存储、可触发、可监控的标准化任务单元。它让 ComfyUI 第一次具备了“任务队列管理”“参数模板复用”“输出自动归档”“失败自动重试”这些只有在成熟 CI/CD 或媒体处理平台里才常见的能力。换句话说它没给 ComfyUI 加新功能却让它从“实验室原型”蜕变成了“生产环境可用的图像生成服务”。我试过用它跑一个含 23 个节点、嵌套 4 层条件控制、需调用 3 类 LoRA 模型的复杂工作流连续生成 876 张不同提示词不同种子不同尺寸的图全程无人值守错误率低于 0.3%日志里每张图的耗时、显存峰值、输入哈希值全都有迹可循。这才是真正意义上的“流水线”——不是形容词是动词是每天能稳定跑满 16 小时的物理存在。这个项目适合三类人直接抄作业第一类是个人创作者想把“灵感→草图→多版渲染→选图→导出”这个闭环压缩到 5 分钟内完成第二类是小型设计团队需要为市场部快速提供 50 套节日海报初稿每套含 3 种尺寸2 种色调变体第三类是技术向 AIGC 工程师正卡在“如何让 ComfyUI 对接 Python 脚本做数据增强”或“怎么把用户提交的 Web 表单自动转成 ComfyUI 执行指令”这类集成问题上。它不教你怎么写 CLIP 文本编码器但会告诉你当你的工作流开始重复超过 5 次就该考虑把它“流水线化”了。2. 核心思路拆解为什么是 WorkBuddy而不是自己写个 Shell 脚本很多人第一反应是“不就是批量跑 ComfyUI 吗我写个 for 循环 curl 调 API 不就完了”——这想法很实在我也这么干过。但三个月后我的脚本文件夹里堆了 17 个版本v1_fix_seed_bug.sh、v2_add_watermark.sh、v3_timeout_retry.sh、v4_multi_gpu_dispatch.sh……每个都解决了一个具体痛点但合起来就是一团意大利面。而 WorkBuddy 的设计哲学恰恰是从根上规避这种“补丁式开发”。它把整个流水线拆成了四个不可拆分的原子层第一层叫任务定义层Task Definition。它强制你用 YAML 写清楚这次要跑哪个工作流 JSON 文件比如product_shot_v2.json输入图放在哪./input/products/提示词模板长什么样A high-res photo of {product}, on white background, studio lighting参数范围怎么扫{product}: [phone_case, laptop_sticker, mug]。这不是配置是契约——契约规定了“什么输入对应什么输出”后续所有自动化都基于此展开。第二层是执行调度层Execution Orchestrator。它不直接调 ComfyUI 的/prompt接口而是先校验任务定义的合法性比如检查 JSON 文件是否存在、提示词占位符是否被正确闭合再根据你预设的 GPU 负载策略如“显存占用超 85% 时暂停新任务”动态排队。我实测过当同时有 12 个高分辨率渲染任务在队列里它能自动把其中 8 个挂起等前 4 个跑完释放显存后再唤醒完全避免 OOM 崩溃——这是纯 curl 脚本永远做不到的“感知式调度”。第三层是上下文管理层Context Broker。这是最容易被忽略、却最体现 WorkBuddy 工程深度的部分。ComfyUI 默认每次执行都是无状态的但真实业务中“这张图必须用和上一张相同的随机种子”“这批图的 LoRA 权重必须保持 0.85 不变”“所有输出图的 EXIF 里要写入项目编号”——这些跨任务的状态WorkBuddy 用轻量级 SQLite 数据库存储并在任务启动时自动注入到工作流 JSON 的对应节点里。比如你在 YAML 里写seed: $CONTEXT.last_used_seed它就会去数据库查上一次成功任务的 seed 值并填进去。这种“状态可追溯、可继承”的能力让流水线不再是孤立任务的集合而成了有记忆、有逻辑的有机体。第四层是可观测性层Observability Layer。它自动生成带时间戳的 HTML 报告页每张图旁边显示执行耗时精确到毫秒、GPU 显存峰值MB、输入参数快照折叠式 JSON、输出图缩略图、以及最关键的——该图在原始工作流 JSON 中对应的节点路径如KSampler_12 seed。当你发现某批图风格突变不用翻几十个日志文件直接点开报告页一眼就能定位是哪个节点的参数被意外覆盖了。所以 WorkBuddy 不是“另一个 ComfyUI 插件”它是给 ComfyUI 装上的工业级操作系统内核。你不用改一行 ComfyUI 源码就能获得企业级任务治理能力。它的价值不在炫技而在把“不确定的手动操作”变成“确定的自动流程”——而这正是所有 AIGC 规模化落地的第一道门槛。3. 核心细节解析YAML 任务定义的 7 个关键字段与避坑指南WorkBuddy 的核心交互入口是一个 YAML 文件它长得像这样# task_product_shots.yaml name: 电商主图批量生成 description: 为 Q3 新品生成白底主图含 3 种产品2 种尺寸 workflow: ./workflows/product_shot_base.json input_dir: ./input/products/ output_dir: ./output/product_shots/{{date}}/ template: prompt: A professional product shot of {product} on pure white background, studio lighting, ultra-detailed, 8k negative_prompt: low quality, blurry, text, watermark, logo parameters: product: [phone_case, laptop_sticker, mug] width: [1024, 2048] height: [1024, 2048] seed: $RANDOM cfg: 7.0 hooks: pre_run: python ./scripts/add_timestamp.py post_process: bash ./scripts/batch_watermark.sh别被表面简洁骗了——这 7 个字段name,description,workflow,input_dir,output_dir,template,parameters,hooks背后全是硬核细节。我来逐个拆解那些文档里不会写的实战要点3.1output_dir的动态路径陷阱output_dir: ./output/product_shots/{{date}}/看似简单但{{date}}不是 Jinja2 模板语法而是 WorkBuddy 自研的轻量解析器。它支持{{date}}格式为2024-07-15、{{time}}14-32-05、{{uuid}}短 UUID但不支持嵌套表达式比如{{date}}_v2是合法的但{{date.split(-)[0]}}会直接报错退出。更关键的是路径权限如果你用sudo workbuddy run -f task.yaml生成的目录所有者会是 root后续普通用户无法写入子文件。我的解决方案是——永远用--user参数指定运行用户workbuddy run -f task.yaml --user $USER。另外output_dir必须以/结尾否则 WorkBuddy 会把它当成文件名而非目录导致所有输出图被强行写进一个叫2024-07-15的二进制文件里我踩过这个坑恢复数据花了两小时。3.2template.prompt的占位符安全边界prompt: A professional product shot of {product}...这里的{product}是标准 Python format 占位符但 WorkBuddy 对其做了严格沙箱限制只允许传入parameters字段里定义的键值对且值必须是字符串、数字或布尔值。如果你在parameters里写了product: [phone_case, {id: 123}]那么当遍历到{id: 123}时WorkBuddy 会直接跳过该组合日志里只写WARN: parameter product value is not string/number/bool, skipped。这看似保守实则是防爆雷——想象一下如果允许传入任意对象恶意构造的product: ${os.system(rm -rf /)}就可能触发远程代码执行虽然 WorkBuddy 本身做了进程隔离但安全边界宁紧勿松。3.3parameters的笛卡尔积爆炸预警product: [phone_case, laptop_sticker, mug]和width: [1024, 2048]组合会产生 3×26 个任务实例。这很直观。但当你加上seed: $RANDOM事情就变了$RANDOM是一个特殊标记表示“为每个任务实例生成独立随机数”它不参与笛卡尔积计算而是作为后置注入项。也就是说6 个任务实例每个都会得到自己独立的 seed总共还是 6 个任务。但如果你写成seed: [123, 456, 789]那就变成 3×2×318 个任务。我见过最狠的案例某用户把prompt_suffix: [, _v2, _final, _alt1, _alt2, _alt3, _alt4, _alt5]和style: [realistic, anime, 3d_render, sketch]一起用参数组合数直接飙到 8×432再乘以width和height的 4 种组合总任务数 128——结果显存不够前 20 个任务全卡死在 KSampler 节点队列假死。我的建议是用workbuddy dry-run -f task.yaml先看预估任务数超过 50 就拆分成多个 YAML 文件用workbuddy batch-run分批执行。3.4hooks的执行时机与错误传播pre_run和post_process看似只是钩子但它们的错误处理逻辑直接影响整个流水线健壮性。pre_run脚本如果返回非零退出码比如exit 1WorkBuddy 会立即终止当前任务实例不进入 ComfyUI 执行阶段并在日志中标记HOOK_FAILED。这非常有用——比如你可以写个pre_run脚本检查输入图尺寸是否大于 8000px超了就自动缩放缩放失败就中断避免 ComfyUI 因超大图直接 OOM。而post_process的行为相反即使它失败WorkBuddy 仍会把该任务标记为SUCCESS因为 ComfyUI 已成功出图但会在报告页里用红色标注POST_PROCESS_FAILED并附上错误日志。这种设计很务实图像生成是核心后处理是锦上添花不能因水印脚本崩溃就让整张图报废。提示所有 hooks 脚本的当前工作目录cwd是 WorkBuddy 的安装目录不是 YAML 文件所在目录。所以./scripts/add_timestamp.py实际执行路径是/path/to/workbuddy/scripts/add_timestamp.py。如果你的脚本依赖相对路径的资源文件务必在脚本开头用cd $(dirname $0)/..切回根目录否则会找不到config.yaml。3.5workflow文件的节点 ID 锁定机制ComfyUI 工作流 JSON 里每个节点都有唯一 ID如12: {class_type: KSampler, ...}。WorkBuddy 在加载 workflow 时会静态解析所有节点 ID并在执行时严格校验。这意味着如果你用 ComfyUI UI 点击“重新随机 ID”再保存 JSON旧的 YAML 任务定义里通过node_id注入参数的方式就会全部失效。WorkBuddy 的解决方案是引入node_alias机制在 YAML 里你可以写parameters: base_seed: 12345 # 将参数注入到 alias 为 sampler 的节点 inject: sampler: seed: {{base_seed}}然后在 workflow JSON 的对应节点里加一个alias: sampler字段。这样即使节点 ID 变了只要 alias 不变注入就依然有效。这是 WorkBuddy 最被低估的工程巧思——它把脆弱的 ID 依赖转化成了稳定的语义依赖。4. 实操全流程从零部署到跑通第一条流水线含完整命令与参数说明现在我们动手把这条流水线跑起来。整个过程分为四个阶段环境准备、WorkBuddy 安装、ComfyUI 配置、任务执行。我会给出每一步的精确命令、预期输出、以及为什么必须这么做。4.1 环境准备Python 3.10 与 CUDA 驱动的黄金组合WorkBuddy 本身是 Python 写的但它重度依赖 ComfyUI 的 Python 环境。所以第一步不是装 WorkBuddy而是确保 ComfyUI 能稳定运行。我推荐的环境组合是操作系统Ubuntu 22.04 LTSWindows 用户请用 WSL2原生 Windows 支持不稳定Python 版本3.10.12注意3.11 会导致某些 ComfyUI 插件兼容问题3.9 太老缺少 asyncio 改进CUDA 版本12.1匹配 PyTorch 2.1.0cu121NVIDIA 驱动535.104.05这是目前最稳的驱动545 系列有已知的显存泄漏 bug验证方式# 检查 Python python3 --version # 必须输出 Python 3.10.x python3 -c import sys; print(sys.path) | grep -q python3.10 || echo ERROR: Not using Python 3.10 # 检查 CUDA nvidia-smi --query-gpuname,driver_version --formatcsv | tail -n 2 | awk -F, {print $2} | grep -q 535.104.05 || echo WARN: Driver version may cause instability # 检查 PyTorch CUDA python3 -c import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.version.cuda) # 预期输出2.1.0cu121, True, 12.1为什么强调这个组合因为 WorkBuddy 的调度器会实时读取nvidia-smi输出来监控 GPU 状态如果驱动版本不对nvidia-smi的 CSV 格式会有微小差异导致 WorkBuddy 解析失败队列永远卡在“waiting for GPU”。这不是 Bug是设计约束——它选择适配最稳定的硬件栈而不是写一堆兼容分支。4.2 WorkBuddy 安装pip install 之外的两个隐藏步骤官方文档说pip install workbuddy就完事但实际部署中有 90% 的失败都卡在这两个隐藏步骤第一步创建专用虚拟环境python3 -m venv /opt/workbuddy-env source /opt/workbuddy-env/bin/activate pip install --upgrade pip setuptools wheel为什么不用系统 Python因为 WorkBuddy 依赖psutil监控进程、pyyaml解析配置、requests调 ComfyUI API这些库的版本和 ComfyUI 环境里的可能冲突。专用环境是隔离风险的底线。第二步配置 ComfyUI 的 API 访问凭证WorkBuddy 默认通过http://127.0.0.1:8188/prompt调用 ComfyUI但 ComfyUI 1.3 默认启用了 API 密钥认证。你必须在 ComfyUI 的extra_model_paths.yaml同级目录下创建api_config.yaml# /path/to/comfyui/api_config.yaml enable: true username: workbuddy password: your_strong_password_here然后重启 ComfyUI。接着在 WorkBuddy 的配置里指定workbuddy config set comfyui.url http://127.0.0.1:8188 workbuddy config set comfyui.auth workbuddy:your_strong_password_here漏掉这步WorkBuddy 会一直报HTTP 401 Unauthorized但错误日志里只写Failed to connect to ComfyUI根本看不出是认证问题。这是新手最常卡住的点。4.3 ComfyUI 工作流 JSON 的“流水线友好”改造随便拿一个 ComfyUI 工作流 JSON 直接用大概率会失败。必须做三处改造改造一为关键节点添加alias打开你的product_shot_base.json找到 KSampler 节点通常是class_type: KSampler在它的inputs下方添加一行alias: sampler同理为 LoadImage 节点加alias: loader为 SaveImage 节点加alias: saver。这样你才能在 YAML 里用inject精准控制。改造二移除所有硬编码路径ComfyUI 工作流里常见image: /home/user/comfyui/input/test.png这种绝对路径。必须删掉改成空值或占位符{ class_type: LoadImage, inputs: { image: // 改为空字符串由 WorkBuddy 在运行时注入 } }改造三设置合理的超时与重试在工作流 JSON 的顶层添加workbuddy_config字段{ workbuddy_config: { timeout_seconds: 300, max_retries: 2, retry_delay_seconds: 10 }, nodes: [ ... ] }timeout_seconds是单次执行最大耗时单位秒超过则强制终止max_retries是失败后重试次数。这两个值必须根据你的 GPU 和工作流复杂度调优。我测试过 RTX 4090 上跑 1024x1024 图timeout_seconds设 180 就够但如果是 2048x2048 30 步采样就得设到 420。设太小会误判超时设太大会让失败任务卡死队列。4.4 执行第一条流水线dry-run → run → monitor 全流程现在万事俱备执行# 第一步预演看任务数和参数组合 workbuddy dry-run -f ./tasks/task_product_shots.yaml # 预期输出 # Dry run for task 电商主图批量生成 # Total tasks to generate: 6 # Parameters grid: # product: [phone_case, laptop_sticker, mug] # width: [1024, 2048] # height: [1024, 2048] # seed: $RANDOM (dynamic) # Output directory: ./output/product_shots/2024-07-15/确认无误后正式运行# 第二步后台运行输出日志到文件 nohup workbuddy run -f ./tasks/task_product_shots.yaml --log-level INFO ./logs/product_shots.log 21 # 第三步实时监控新开终端 workbuddy monitor --task-name 电商主图批量生成monitor命令会实时刷新一个 TUI 界面显示当前队列长度、正在运行的任务数、已完成/失败/等待中的任务数、GPU 显存使用率、最近 5 条错误日志。当看到Completed: 6/6时去./output/product_shots/2024-07-15/目录你会看到phone_case_1024x1024_123456789.png phone_case_1024x1024_987654321.png laptop_sticker_2048x2048_456789123.png ...每个文件名都包含关键参数信息方便溯源。而完整的 HTML 报告页在./output/product_shots/2024-07-15/report.html打开就能看到所有图的缩略图和详细元数据。注意workbuddy run命令默认是阻塞式的即终端会一直挂着直到所有任务完成。生产环境必须用nohup或systemd启动否则关掉终端就中断了。这也是为什么monitor是独立命令——它连接的是 WorkBuddy 的内部状态服务不依赖主进程是否存活。5. 常见问题与排查技巧实录那些文档里绝不会写的血泪经验在帮 37 个不同行业的用户部署 WorkBuddy 流水线后我整理出这份高频问题清单。每个问题都附带真实错误日志、根本原因、以及一句能救命的排查口诀。5.1 问题ERROR: Failed to connect to ComfyUI at http://127.0.0.1:8188但curl http://127.0.0.1:8188返回正常错误日志特征[ERROR] ConnectionError: HTTPConnectionPool(host127.0.0.1, port8188): Max retries exceeded with url: /prompt (Caused by NewConnectionError(urllib3.connection.HTTPConnection object at 0x...: Failed to establish a new connection: [Errno 111] Connection refused))根本原因ComfyUI 进程确实在运行但 WorkBuddy 尝试连接时ComfyUI 正处于“热身”状态——它刚启动Web 服务器已监听端口但内部模型加载、节点注册等初始化还没完成/prompt接口暂时不可用。WorkBuddy 的默认重试策略是 3 次间隔 1 秒但有些大模型加载要 20 秒以上。排查口诀“先看 ComfyUI 日志末尾有没有Model loaded再看 WorkBuddy 是否在 ComfyUI 完全就绪前就发请求。”解决方案在workbuddy run命令前加sleep 30 或者用workbuddy config set comfyui.startup_delay 30设置全局启动延迟。更优雅的做法是写个健康检查脚本# wait_comfy_ready.sh while ! curl -sf http://127.0.0.1:8188/system_stats /dev/null; do echo Waiting for ComfyUI... sleep 5 done echo ComfyUI is ready!然后bash wait_comfy_ready.sh workbuddy run -f task.yaml。5.2 问题流水线跑着跑着突然卡住workbuddy monitor显示Running: 1但 GPU 显存占用为 0%错误日志特征[INFO] Task xxx started with PID 12345之后再无任何日志nvidia-smi显示 GPU Memory-Usage 为0MiB / 24576MiB。根本原因这是 ComfyUI 的经典“静默卡死”现象。通常发生在工作流里用了某个不稳定的自定义节点比如一个未处理异常的 ControlNet 节点或者输入图损坏如 PNG 文件头被截断ComfyUI 解码时报错但没抛出异常进程僵死WorkBuddy 的进程监控只看 PID 是否存活不检测进程是否真在干活。排查口诀“卡死必查 PID 对应进程的 CPU 占用率0% 就是僵死100% 才是真干活。”解决方案ps aux | grep 12345找到卡死进程top -p 12345看 CPU%如果是 0%执行kill -9 12345强杀WorkBuddy 会自动检测到进程消失标记该任务为FAILED并启动重试如果配置了max_retries永久修复在workbuddy config里设置process.watchdog_timeout 120单位秒开启看门狗。一旦检测到某任务进程 CPU 占用持续 120 秒为 0%自动强杀并重试。5.3 问题输出图全是黑的或纯色但 WorkBuddy 日志显示SUCCESS错误日志特征[INFO] Task xxx completed in 42.3s[INFO] Output saved to ./output/xxx.png但打开 PNG 文件是 1024x1024 的纯黑色。根本原因ComfyUI 的 SaveImage 节点默认保存为 PNG但某些工作流里SaveImage 的filename_prefix被设为空字符串导致 ComfyUI 用默认前缀ComfyUI_保存而 WorkBuddy 期望的文件名是xxx.png。于是 WorkBuddy 认为“文件已生成”其实它生成的是ComfyUI_xxx.png而xxx.png是个空文件。排查口诀“黑图先看输出目录里有没有 ComfyUI_ 开头的文件有就是 SaveImage 前缀没对齐。”解决方案修改工作流 JSON 里的 SaveImage 节点把filename_prefix设为并确保output_dir在 YAML 里配置正确。更保险的做法是在 YAML 的hooks.post_process里加一步重命名# ./scripts/fix_filename.sh for f in ./output/product_shots/*/ComfyUI_*.png; do if [ -f $f ]; then mv $f $(dirname $f)/$(basename $f | sed s/ComfyUI_//) fi done5.4 问题workbuddy monitor报错sqlite3.OperationalError: database is locked错误日志特征[ERROR] sqlite3.OperationalError: database is locked出现在高并发任务20 个场景下。根本原因WorkBuddy 用 SQLite 存储任务状态SQLite 的 WAL 模式在高写入频率下如果多个进程同时尝试写入会出现锁等待超时。这不是 WorkBuddy 的 Bug是 SQLite 的设计特性。排查口诀“锁库必查是否同时运行多个 workbuddy runSQLite 不支持多进程写。”解决方案永远不要同时运行多个workbuddy run进程。所有批量任务必须用workbuddy batch-run合并到一个 YAML 文件里由单个进程调度。如果真需要多机协同WorkBuddy 提供了--redis-url redis://localhost:6379参数可切换到 Redis 后端需自行部署 Redis。5.5 问题中文提示词乱码输出图里文字全是方块错误日志特征无错误日志但生成的图里中文显示为 □□□。根本原因ComfyUI 默认用系统字体渲染文字Linux 服务器通常没装中文字体。WorkBuddy 本身不处理字体它只是把提示词原样传给 ComfyUI。排查口诀“乱码不怪 WorkBuddy怪 ComfyUI 的字体路径没配对。”解决方案在服务器上安装思源黑体sudo apt install fonts-noto-cjk修改 ComfyUI 的custom_nodes/efficiency-nodes-comfyui如果用了效率节点或相关文本渲染节点的配置指定字体路径/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc或者更简单在提示词里用英文替代比如把“中国风山水画”写成Chinese style landscape painting, ink wash, traditional。6. 进阶应用把流水线接入你的工作流CI/CD、Webhook、定时任务WorkBuddy 的终极价值不是让你少点几次鼠标而是让你的 AIGC 能力成为可编程的基础设施。这里分享三个真实落地的进阶场景每个都附可直接运行的代码。6.1 场景一Git Push 触发自动渲染CI/CD 集成你想实现设计师把新商品图product_new.jpg提交到 Git 仓库的input/目录CI 系统自动触发 WorkBuddy 渲染并把结果推回output/目录。这需要三步第一步配置 Git Hook服务端在 Git 仓库的hooks/post-receive里加#!/bin/bash GIT_WORK_TREE/path/to/your/repo git checkout -f cd /path/to/your/repo # 检查是否有新图片 if git diff HEAD{1} HEAD --quiet input/*.jpg; then echo No new images, skip. else echo New images detected, triggering WorkBuddy... # 生成临时 YAML cat /tmp/auto_task.yaml EOF name: Auto-render from Git workflow: ./workflows/product_shot_base.json input_dir: ./input/ output_dir: ./output/{{date}}/ template: prompt: A professional product shot of {filename} on white background parameters: filename: $(git diff --name-only HEAD{1} HEAD | grep input/ | head -1 | sed s/input\///) EOF # 执行 /opt/workbuddy-env/bin/workbuddy run -f /tmp/auto_task.yaml fi第二步WorkBuddy 任务完成后自动 Commit在 YAML 的hooks.post_process里写#!/bin/bash cd /path/to/your/repo git add output/{{date}}/ git commit -m Auto-render: $(date) git push origin main这样设计师只要git add input/new.jpg git commit -m add new product git push5 分钟后output/2024-07-15/就有了渲染好的图。6.2 场景二Webhook 接收表单动态生成任务你有个内部网站市场部填个表单“产品名”、“主色调”、“尺寸”点提交就生成图。后端用 Flask 接收# webhook_server.py from flask import Flask, request, jsonify import subprocess import json import tempfile import os app Flask(__name__) app.route(/render, methods[POST]) def trigger_render(): data request.json # 动态生成 YAML yaml_content fname: Webhook render workflow: ./workflows/product_shot_base.json input_dir: ./input/ output_dir: ./output/webhook_{{date}}/ template: prompt: A professional product shot of {data[product]} in {data[color]} color, on white background parameters: width: [{data[width]}] height: [{data[height]}] seed: $RANDOM # 写入临时文件 with tempfile.NamedTemporaryFile(modew, suffix.yaml, deleteFalse) as f: f.write(yaml_content) temp_yaml f.name # 调用 WorkBuddy result subprocess.run( [/opt/workbuddy-env/bin/workbuddy, run, -f,