agentscope-ai / agentscope-ai/agentscope-java

[Bug]:主agent停止子agent不停

オープン
#1,577 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
area/harness bug
主要言語
Java
スター
5.6k
フォーク
1.3k
平均マージ
4日 12時間
マージ済み PR(30日)
77

説明

你的判断是对的。这是一个**真实存在的取消传播断裂问题**。

## 问题本质

Reactor 的取消信号传播依赖于**完整的订阅链**——从下游 `Subscriber` 到上游 `Publisher`,`cancel()` 信号沿 `Subscription` 逐级回传。一旦链中任何环节调用了 `.subscribe()` 且未保存 `Disposable`,就等于开了一个"脱管的火", cancel 信号无法穿越这个边界。

在 [AgentSpawnTool.execLocalSync](agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/AgentSpawnTool.java#L454-L507) 中,子 Agent 的调用链设计为:

```
父 Mono 链 → execLocalSync (deferContextual) → invokeAgentStream → doOnNext(bus::emit) → .last() → .map()
```

这条链本身是纯响应式操作符组合,cancel 信号**理论上**可以传播。但问题出在 `AgentBase.createEventStream()`——根据其 Javadoc(见 [AgentSpawnTool.java#L60-L67](agentscope-harness/src/main/java/io/agentscope/harness/agent/tool/AgentSpawnTool.java#L60-L67)):

> *"When a `SubagentEventBus` is present in the Reactor Context (injected by `AgentBase.createEventStream`)..."*

`createEventStream()` 是设置 `SubagentEventBus` 并注入到 Reactor Context 的入口。如果它内部对 Flux 做了 `.subscribe()` 来桥接事件而**没保存 Disposable**,那么:

1. 父 Agent 响应式链被 cancel → 信号传到 `execLocalSync` 的 `deferContextual`
2. 但 `invokeAgentStream` 内部如果已经通过 `.subscribe()` 消费了子 Agent 的事件流
3. 那个 `.subscribe()` 创建了**独立的订阅**,与父链无关
4. **cancel 信号无法到达子 Agent 的实际执行流** → 子 Agent 继续运行

## 影响范围

| 场景 | 是否受影响 |
|---|---|
| `call()` 模式(非 streaming) | ⚠️ 不受影响——走 `invokeAgent` 非流式路径,没有 `.subscribe()` 桥接 |
| `stream()` 模式 + 子 Agent | ❌ **受影响**——`createEventStream` 内部的 `.subscribe()` 打断链路 |
| 异步子 Agent(timeout=0) | ❌ 本来就不在链上,不受影响(需 `task_cancel`) |

## 修复方向

要修复这个问题,有两个思路:

**思路 A:保存 Disposable,在父链 cancel 时主动 dispose**

```java
// createEventStream 内部应类似:
Disposable childSub = childFlux.subscribe(sink::next);
// 在父链的 onCleanup/doFinally 中:
childSub.dispose();
```

**思路 B:避免手动 subscribe,用响应式操作符桥接**

```java
// 不用 .subscribe(),而是将子 Flux 直接嵌入父链:
childFlux.doOnNext(bus::emit)
.then(mainChain); // 通过操作符组合保持链路完整
```

思路 B 更符合 Reactor 的编程范式——**能用操作符就不要手动 subscribe**。手动 subscribe 是响应式编程中的"goto",一旦用了就得自己管生命周期。

---

你抓到了一个值得提 issue 的 bug。子 Agent 不随父 Agent 停止,在生产环境下不仅是资源泄漏,还可能导致幽灵任务持续消耗 token。

建议深入阅读:
- [钩子系统与事件](9-hook-system-and-events) — Hook 中可注入清理逻辑作为临时缓解方案
- [子 Agent 管理](24-subagent-management) — 子 Agent 生命周期完整文档
- [中断与人机协同](10-interruption-and-human-in-the-loop) — interrupt 机制与 cancel 传播的交互

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。