agentscope-ai / agentscope-ai/QwenPaw

[Creator] 多图生成期间用户「审核通过」会中断正在执行的图片任务且不重新调度,任务永久卡在 RUNNING

Ouverte
#7,693 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub
Langage dominant
Python
Étoiles
34.9k
Forks
3.1k
Merge moyen
1 j 15 h
PR mergées (30 j)
225

Description

# [Creator] 多图生成期间用户「审核通过」会中断正在执行的图片任务且不重新调度,任务永久卡在 RUNNING

> 复制以下全部内容到 https://github.com/agentscope-ai/QwenPaw/issues/new
> 标题:`[Creator] 多图生成期间用户「审核通过」会中断正在执行的图片任务且不重新调度,任务永久卡在 RUNNING`

---

## 摘要

Creator 在多图项目里会**严格串行**生成图片(并发槽 `model_slot("image")` limit = 1,每张约 45–100 秒),这一串行机制本身工作正常。

真正的 bug 是:**当某张图片正在生成时,如果用户提交一次审核决策(review ACCEPT,即界面上的「审核通过 / 承认」),正在执行的这次图片生成会被打断 —— HTTP 请求被丢弃、永远不会返回 —— 并且该任务不会被重新调度,永久停留在 `RUNNING`(界面显示 pending)。排队中的其余图片任务也随之永远不被唤醒。**

由于 Creator 的工作流本身要求用户点「审核通过」才能推进到下一阶段(work-graph 节点会 `waits for async review`),**该 bug 在正常使用路径下 100% 复现**。

## 环境

| 项目 | 版本 / 配置 |
|------|-------------|
| QwenPaw | 2.2.0 |
| Creator | 1.1.1(插件 `qwenpaw-creator`) |
| 图片模型 | `qwen-image-3.0-pro`(DashScope / 百炼,multimodal-generation) |
| LLM | `deepseek-chat` |
| 系统 | Linux(kernel 5.4),Python 3.12 |
| 并发配置 | `DASHSCOPE_IMAGE_CONCURRENCY=1`、`IMAGE_CONCURRENCY=1` |
| 依赖 | `jq` / `ffmpeg` / `ffprobe` 均 `ok`;启动日志 `0 integrity errors` |

## 复现步骤

1. 打开 QwenPaw Creator,新建一个需要 **≥2 张图片**(如 1 个角色 + 2 个道具/场景,或资产图 + 分镜图)的项目。
2. 启动生成,让图片任务进入串行生成(第 1 张开始生成、其余排队)。
3. **在第 1 张图片尚未生成完成时,点击「审核通过 / 承认」。**
4. 观察后续生成。

## 预期行为

- 审核决策不应影响已经提交到 provider 的图片生成请求;
- 即便被打断,任务也应被重新调度(幂等重试)或明确置为失败,而不是永久 `RUNNING`。

## 实际行为

- 审核通过后,**正在执行的那次图片生成立刻被中断**(并发槽被 `RELEASE`,但**没有对应的 HTTP 响应返回**);
- 任务永久卡在 `RUNNING`(界面 pending),`finished_at = null`;
- 排队中的其余图片任务**再也没有被唤醒**;
- 等待 >10 分钟无任何变化,只能手动取消。

## 对照实验(决定性)

在同一个实例上做了干净的两组对照,把「并发链路」和「审核交互」两个变量分开:

| 实验 | 操作 | 结果 |
|------|------|------|
| **A** | 3 张资产图(cat / scooter / park)生成期间**全程不碰 UI** | ✅ 3 张全部依次生成成功(串行,分别耗时 ~94s / ~45s / ~40s) |
| **B** | 图片生成期间**点击「审核通过」** | ❌ 正在执行的图片任务被中断,后续任务永久挂起 |

→ **并发槽机制没有问题**(A 证明),**问题出在审核交互打断执行中的图片任务**(B 证明)。

## 关键证据(探针日志)

> 为定位问题,在插件 3 个文件里临时埋了探针(`models/concurrency.py` 的 `model_slot WAIT/ACQUIRED/RELEASE`、`models/image/base.py` 的 `IMAGE PROBE`、`services/media_files/image_execution.py` 的 `provider.generate` 前后),随一次重启生效。原文件已备份,可随时还原。

### 实验 A:不动 UI(项目 `project-09dae3cf08b455e2985ba6820e518544`,全部成功)

```
05:45:05 IMAGE PROBE | calling provider.generate | target=asset:char:cat
05:45:05 model_slot WAIT | kind=image limit=1 value=1 waiters=0
05:45:05 model_slot ACQUIRED | kind=image limit=1 value=0
05:45:05 IMAGE PROBE | sending request
05:45:05 IMAGE PROBE | calling provider.generate | target=asset:prop:scooter → model_slot WAIT
05:45:05 IMAGE PROBE | calling provider.generate | target=asset:scene:park → model_slot WAIT (waiters=1)
05:46:39 IMAGE PROBE | request returned | status=200 ← cat 完成(~94s)
05:46:39 model_slot RELEASE
05:46:39 model_slot ACQUIRED → scooter 被正确唤醒
05:47:24 IMAGE PROBE | request returned | status=200 ← scooter 完成(~45s)
05:47:24 model_slot ACQUIRED → park 被正确唤醒
05:48:04 IMAGE PROBE | request returned | status=200 ← park 完成(~40s)
05:48:05 model_slot RELEASE
```

✅ 结论:3 张图**全部生成成功**,串行调度、唤醒逻辑完全正常。

### 实验 B:生成期间点「审核通过」→ 任务被中断且不再重排(项目 `project-a53c001147e55009991c5437b1088fab`)

```
05:30:17 cat 任务创建,进入 provider.generate
05:30:18 cat 拿到并发槽(limit=1),发出请求
05:31:16 scooter 任务创建 → model_slot WAIT(排队)
05:31:24 park 任务创建 → model_slot WAIT(waiters=1)
05:31:59 IMAGE PROBE | request returned | status=200 ← cat 完成(耗时 101s),保存图片 1.62MB
05:31:59 model_slot RELEASE
05:31:59 model_slot ACQUIRED → scooter 被正确唤醒,发出请求 ✅ 到这里一切正常
──────────────────────────────────────────────────────────────
05:32:20 review decided(6 × operation=ACCEPT) ← ★ 用户点了「审核通过」
05:32:20 goal status: RESUME_REQUIRED → ACTIVE ← ★ 同一秒,agent 主循环被恢复
05:32:20 model_slot RELEASE | kind=image ← ★ 同一秒,图片槽被释放
(但此后再无 "IMAGE PROBE | request returned" —— 请求被中断,从未返回)
──────────────────────────────────────────────────────────────
05:32:20 → 05:38:15 💀 死寂:无任何 image 活动,park 从未被唤醒
05:38:15 用户手动取消 → scooter / park 标记 USER_CANCELLED
```

对应 `qwenpaw.log`:

```
05:32:20 | review decided: project=project-a53c0011... review=review-round-181ddb8a...
decisions=operation-5bfea95d...=ACCEPT, operation-89b77690...=ACCEPT, ...(共 6 个 ACCEPT)
05:32:20 | goal status: ... status=RESUME_REQUIRED
05:32:20 | goal status: ... status=ACTIVE
```

❌ 结论:审核通过触发的 agent 主循环恢复,**打断了正在执行的图片任务,且中断后未重新调度**。

## 根因分析

1. Creator 的图片生成是**严格串行**的:`async with model_slot("image")`,limit = 1,每张约 45–100 秒。**该机制本身工作正常**(实验 A 已证)。
2. 用户的审核决策会触发 `goal: RESUME_REQUIRED → ACTIVE`,即 **agent 主循环从挂起状态恢复**。
3. 这个恢复动作**与正在执行的图片任务共用同一执行上下文**,导致在途的图片任务被打断(并发槽被释放,但在途 HTTP 请求被丢弃、无响应返回)。
4. **被打断的任务不会被重新调度**,于是永久停在 `RUNNING`;排队中的任务也一并中断(未被唤醒)。
5. 由于 Creator 工作流的节点本身 `waits for async review`(必须人工审核通过才能推进),**正常使用路径下必现**。

**已排除的假设**(均被实验证伪):
- ❌ 并发信号量死锁 / 卡死
- ❌ `DASHSCOPE_IMAGE_CONCURRENCY` 被覆盖、limit 变成 5 或抖动(实测 `get_image_concurrency() = 1`)
- ❌ 环境变量未生效 / 配置问题(`.env`、`qwenpaw env list`、进程内独立验证三处一致)
- ❌ LLM 模型导致的(已从 `deepseek-v4-flash` 切换到 `deepseek-chat`,问题依旧)
- ❌ 缺少 `jq` / `ffmpeg`(均已就绪,问题依旧)

## 影响

- 多图项目(资产图 + 分镜图场景下尤其明显)**必然卡死**,功能实际不可用;
- 被打断的图片请求在 **DashScope 侧可能仍继续生成并计费**(产生拿不到的「孤儿」结果,白耗额度);
- 任务永久 `RUNNING`,**没有超时机制**,用户只能手动取消;
- 现象具有很强误导性 —— 看起来像「并发 bug / 第 2 张图不生成」,实际与并发无关。

## 建议的修复方向

1. **隔离执行上下文**:审核决策 / 主循环恢复不应打断已提交到 provider 的图片任务(等待其完成,或在独立 task 中运行图片任务)。
2. **中断后必须重新调度**:被打断的图片任务应幂等重试或明确置为失败,而**不是永久挂起**。
3. **增加超时**:图片任务超过 N 分钟未返回应自动失败并重试,避免永久 `RUNNING`。
4. **状态可见性**:界面应区分「正在生成」与「已挂起/排队中」,不要都显示成 pending。

## 规避方法(workaround)

- 多图项目生成期间**全程不要点击任何 UI**(不审核、不通过、不发消息、不刷新、不切项目),等所有图片出完再统一审核;或
- 绕过 Creator 的图片生成链路,直接用 DashScope `multimodal-generation` 接口(`qwen-image-*`)逐张出图。

## 备注

- 复现项目 ID:`project-09dae3cf08b455e2985ba6820e518544`(实验 A,成功)、`project-a53c001147e55009991c5437b1088fab`(实验 B,被打断;项目现已删除)。
- 日志来源:`~/qwenpaw/creator-runtime/observability/logs/creator.log`、`~/qwenpaw/qwenpaw.log`。
- 如需完整原始日志 / 探针补丁,可另行提供。

---

### 版本补充说明

- 本 issue 复现于 **Creator 1.1.1 / QwenPaw 2.2.0**。
- 注意到 `#7486`(Creator 1.2.0)提到 "async delegation" / "in-process locking" / "async media generation",若相关改动已触及图片任务的执行上下文隔离与重调度,可能本问题已修复 —— 麻烦确认后决定是否需要复测。
- 本次定位所用的探针补丁(`models/concurrency.py`、`models/image/base.py`、`services/media_files/image_execution.py`)为临时改动、原文件已备份,如需可提供。

Guide de contribution

Ouvrir le guide de contribution

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.