RFC: thread suspend/resume for pending information #587

Closed
opened 2026-06-02 03:32:29 +00:00 by xiaomo · 3 comments
Owner

Problem

当 workflow 执行中缺乏信息(如 planner 发现 issue 描述不充分),当前只能走 $END 终止整个 workflow。这意味着:

  1. 补充信息后需要重新 start 一个新 thread,丢失已有上下文
  2. 无法在原 thread 上继续执行
  3. planner 只能选择「够了就继续」或「不够就结束」,没有中间态

Proposal

引入 suspend 状态,允许 thread 挂起等待外部输入,然后 resume 继续。

新状态:suspended

在现有 idle / running / done 之外,新增 suspended 状态。

挂起方式

Role 输出 $status: suspended 时,thread 进入挂起态,记录:

  • 当前 role(resume 后回到这个 role)
  • 挂起原因(agent 输出的 message/reason)

恢复方式

uwf thread resume <thread-id> -p "补充的信息"

补充的信息作为新的 prompt context 注入,thread 回到挂起时的 role 继续执行。

Graph 路由

graph:
  planner:
    suspended:
      role: $SUSPEND   # 新的特殊 target
      prompt: "Waiting for additional information: {{{reason}}}"
    ready:
      role: developer
      prompt: ...

或者更简单的方式:$SUSPEND 作为保留 status,不需要在 graph 里显式声明——任何 role 输出 $status: suspended 都会自动挂起。

待讨论

  1. $SUSPEND 是 graph target 还是隐式 status? — graph target 更显式,但每个可能挂起的 role 都要声明;隐式更简洁但不可见
  2. resume 时注入方式 — 作为新 step 的 prompt?作为 context 追加到原 prompt?
  3. 多次 suspend — 同一 thread 能否多次挂起?(应该可以)
  4. 超时/过期 — suspended thread 是否需要 TTL?
  5. CAS 记录 — suspend 事件是否需要写入 step chain?

用例

  • solve-issue: planner 发现 issue 信息不足 → suspend → 用户补充 → resume
  • review-code: reviewer 需要作者解释设计决策 → suspend → 作者回复 → resume
  • 任何需要 human-in-the-loop 的场景
## Problem 当 workflow 执行中缺乏信息(如 planner 发现 issue 描述不充分),当前只能走 `$END` 终止整个 workflow。这意味着: 1. 补充信息后需要重新 start 一个新 thread,丢失已有上下文 2. 无法在原 thread 上继续执行 3. planner 只能选择「够了就继续」或「不够就结束」,没有中间态 ## Proposal 引入 **suspend** 状态,允许 thread 挂起等待外部输入,然后 resume 继续。 ### 新状态:`suspended` 在现有 `idle` / `running` / `done` 之外,新增 `suspended` 状态。 ### 挂起方式 Role 输出 `$status: suspended` 时,thread 进入挂起态,记录: - 当前 role(resume 后回到这个 role) - 挂起原因(agent 输出的 message/reason) ### 恢复方式 ```bash uwf thread resume <thread-id> -p "补充的信息" ``` 补充的信息作为新的 prompt context 注入,thread 回到挂起时的 role 继续执行。 ### Graph 路由 ```yaml graph: planner: suspended: role: $SUSPEND # 新的特殊 target prompt: "Waiting for additional information: {{{reason}}}" ready: role: developer prompt: ... ``` 或者更简单的方式:`$SUSPEND` 作为保留 status,不需要在 graph 里显式声明——任何 role 输出 `$status: suspended` 都会自动挂起。 ## 待讨论 1. **`$SUSPEND` 是 graph target 还是隐式 status?** — graph target 更显式,但每个可能挂起的 role 都要声明;隐式更简洁但不可见 2. **resume 时注入方式** — 作为新 step 的 prompt?作为 context 追加到原 prompt? 3. **多次 suspend** — 同一 thread 能否多次挂起?(应该可以) 4. **超时/过期** — suspended thread 是否需要 TTL? 5. **CAS 记录** — suspend 事件是否需要写入 step chain? ## 用例 - solve-issue: planner 发现 issue 信息不足 → suspend → 用户补充 → resume - review-code: reviewer 需要作者解释设计决策 → suspend → 作者回复 → resume - 任何需要 human-in-the-loop 的场景
Owner
  1. suspend 应该是一个隐式的 status,graph 里应该没有一个类似 $START/$END 的 $SUSPEND 节点 ,而是在任何一个 role 上都可以有一个 suspend 状态,resume 了还是回到这个 role
  2. 应该是作为新的 step,毕竟要重新调起 agent session
  3. 可以多次
  4. suspend 不应该超时,等待人回应,这是异步任务的常态,可能会很久
  5. 应该记录进 CAS,不过具体的方法值得讨论
1. suspend 应该是一个隐式的 status,graph 里应该没有一个类似 $START/$END 的 $SUSPEND 节点 ,而是在任何一个 role 上都可以有一个 suspend 状态,resume 了还是回到这个 role 2. 应该是作为新的 step,毕竟要重新调起 agent session 3. 可以多次 4. suspend 不应该超时,等待人回应,这是异步任务的常态,可能会很久 5. 应该记录进 CAS,不过具体的方法值得讨论
Author
Owner

收到,方向确认:

设计决议

# 决定 说明
1 隐式 status 任何 role 输出 $status: suspended 即挂起,resume 回到同一 role。graph 不需要声明 $SUSPEND target
2 新 step resume 注入的信息作为新 step 写入 chain,重新调起 agent session
3 可多次 同一 thread 可反复 suspend/resume
4 无 TTL suspended thread 不过期,异步等待人回应
5 记录 CAS suspend 事件写入 step chain,具体结构待设计

待细化

suspend step 的 CAS 结构

选项 A:suspend 是一个正常 StepNode,role 就是当前 role,output 记录 suspend reason:

{ role: "planner", $status: "suspended", output: { reason: "需要更多信息" } }

选项 B:suspend 是一个特殊类型的 step(新 schema),和普通 step 区分:

{ type: "suspend", suspendedRole: "planner", reason: "..." }

倾向 A——复用现有 StepNode,suspended 只是一个 status 值。moderator 看到 suspended 就不路由,thread 进入 suspended 态。

resume 流程

uwf thread resume <id> -p "补充信息"
  1. 检查 thread status == suspended
  2. 读取 head step,拿到 suspendedRole
  3. 构造新 prompt = 原 role prompt + resume 补充信息
  4. 以该 role 执行一个新 step
  5. thread 恢复为 idle/running

这样 moderator 逻辑改动最小——只需要在 evaluate 时跳过 suspended status 的路由即可。

主人觉得方向 OK 吗?OK 的话我可以拆 implementation issues。

收到,方向确认: ## 设计决议 | # | 决定 | 说明 | |---|------|------| | 1 | **隐式 status** | 任何 role 输出 `$status: suspended` 即挂起,resume 回到同一 role。graph 不需要声明 `$SUSPEND` target | | 2 | **新 step** | resume 注入的信息作为新 step 写入 chain,重新调起 agent session | | 3 | **可多次** | 同一 thread 可反复 suspend/resume | | 4 | **无 TTL** | suspended thread 不过期,异步等待人回应 | | 5 | **记录 CAS** | suspend 事件写入 step chain,具体结构待设计 | ## 待细化 ### suspend step 的 CAS 结构 选项 A:suspend 是一个正常 StepNode,role 就是当前 role,output 记录 suspend reason: ``` { role: "planner", $status: "suspended", output: { reason: "需要更多信息" } } ``` 选项 B:suspend 是一个特殊类型的 step(新 schema),和普通 step 区分: ``` { type: "suspend", suspendedRole: "planner", reason: "..." } ``` 倾向 A——复用现有 StepNode,`suspended` 只是一个 status 值。moderator 看到 `suspended` 就不路由,thread 进入 suspended 态。 ### resume 流程 ``` uwf thread resume <id> -p "补充信息" ``` 1. 检查 thread status == suspended 2. 读取 head step,拿到 suspendedRole 3. 构造新 prompt = 原 role prompt + resume 补充信息 4. 以该 role 执行一个新 step 5. thread 恢复为 idle/running 这样 moderator 逻辑改动最小——只需要在 evaluate 时跳过 `suspended` status 的路由即可。 主人觉得方向 OK 吗?OK 的话我可以拆 implementation issues。
Author
Owner

理解了,修正设计:

$SUSPEND = 伪 role target(同 $END

$SUSPEND 不是 status 值,而是 graph 里的 target role。跟 $END 一个模式:

graph:
  planner:
    insufficient_info:
      role: $SUSPEND
      prompt: "需要补充以下信息:{{{reason}}}"
    ready:
      role: developer
      prompt: ...
  • Agent 输出 $status: insufficient_info(正常 status)
  • Moderator 路由,发现 target 是 $SUSPEND
  • Thread 进入 suspended 态,记录 suspendedRole = planner
  • Prompt 模板渲染后作为给用户的消息(告知需要什么信息)
  • Resume 时回到 planner 重新执行

优势

  • Workflow 作者显式控制哪些 status 会挂起
  • Prompt 模板定义给用户的提示信息,可用 mustache 插值
  • 同一 role 可以有多个不同 status 路由到 $SUSPEND(不同原因不同提示)
  • Moderator 改动最小——$SUSPEND$END 走类似分支

修正后的设计决议

# 决定
1 $SUSPEND 是 graph 的伪 role target,workflow YAML 显式声明
2 resume 作为新 step 写入 chain
3 可多次 suspend/resume
4 无 TTL
5 suspend 事件记录 CAS(StepNode with suspended status)

方向 OK 的话我拆 issues。

理解了,修正设计: ## `$SUSPEND` = 伪 role target(同 `$END`) `$SUSPEND` 不是 status 值,而是 graph 里的 target role。跟 `$END` 一个模式: ```yaml graph: planner: insufficient_info: role: $SUSPEND prompt: "需要补充以下信息:{{{reason}}}" ready: role: developer prompt: ... ``` - Agent 输出 `$status: insufficient_info`(正常 status) - Moderator 路由,发现 target 是 `$SUSPEND` - Thread 进入 `suspended` 态,记录 `suspendedRole = planner` - Prompt 模板渲染后作为给用户的消息(告知需要什么信息) - Resume 时回到 `planner` 重新执行 ### 优势 - Workflow 作者显式控制哪些 status 会挂起 - Prompt 模板定义给用户的提示信息,可用 mustache 插值 - 同一 role 可以有多个不同 status 路由到 `$SUSPEND`(不同原因不同提示) - Moderator 改动最小——`$SUSPEND` 和 `$END` 走类似分支 ### 修正后的设计决议 | # | 决定 | |---|------| | 1 | `$SUSPEND` 是 graph 的伪 role target,workflow YAML 显式声明 | | 2 | resume 作为新 step 写入 chain | | 3 | 可多次 suspend/resume | | 4 | 无 TTL | | 5 | suspend 事件记录 CAS(StepNode with suspended status) | 方向 OK 的话我拆 issues。
Sign in to join this conversation.
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/workflow#587