agentscope-ai / agentscope-ai/agentscope-java
[Feature]: **Title**: `BaseReActAgentRunner.stream()` returns deprecated coarse-grained `Flux<Event>` and does not surface the fine-grained `AgentEvent` stream **Labels**: `bug` / `enhancement` / `a2a`
- 主要語言
- Java
- 星號
- 5.6k
- 分支
- 1.3k
- 平均合併
- 4 天 12 小時
- 30 天內合併 PR
- 77
描述
## Description
The A2A server's `BaseReActAgentRunner.stream(...)` currently delegates to the **deprecated** `ReActAgent.stream(List)` which returns `Flux` (the coarse-grained v1 event type). As a result, A2A sub-agents cannot emit / consume the fine-grained `AgentEvent` hierarchy (`Flux`) that covers the full agent lifecycle (reasoning chunks, tool calls/results, HITL `RequireUserConfirmEvent`, `AgentResultEvent`, etc.).
### Location
`agentscope-extensions/.../a2a/.../server/executor/runner/BaseReActAgentRunner.java`
```java
@Override
public Flux stream(List requestMessages, AgentRequestOptions options) {
...
ReActAgent agent = buildReActAgent();
agentCache.put(options.getTaskId(), agent);
return agent.stream(requestMessages) // <-- deprecated coarse-grained API
.doFinally(signal -> agentCache.remove(options.getTaskId()));
}
```
`ReActAgent` itself already provides the replacement API:
- `Flux stream(...)` — `@Deprecated(since = "2.0.0", forRemoval = true)`
- `Flux streamEvents(List)` / `streamEvents(List, RuntimeContext)` — the recommended fine-grained stream
### Impact / Why it matters
1. **Lost observability & control**: `AgentEvent` carries the full lifecycle (reasoning chunks, tool invocations, `RequireUserConfirmEvent` for human-in-the-loop, final `AgentResultEvent`). With only `Flux`, A2A handlers (e.g. `AgentScopeAgentExecutor`'s streaming/blocking `FluxEventHandler`) cannot distinguish event semantics reliably and must rely on the legacy `EventType` mapping.
2. **HITL breakage**: When a tool requires confirmation, the fine-grained `RequireUserConfirmEvent` is the only typed signal. The coarse `Event` stream does not expose it cleanly, so A2A callers cannot implement human-in-the-loop approval flows properly.
3. **Forward-compat**: `stream(...)` is marked `forRemoval = true`, so this runner will break once it is removed.
### Proposed fix
Make `BaseReActAgentRunner` (or its `AgentRunner` contract) support `AgentEvent`, e.g.:
```java
@Override
public Flux streamEvents(List requestMessages, AgentRequestOptions options) {
if (agentCache.containsKey(options.getTaskId())) {
throw new IllegalStateException("Agent already exists for taskId: " + options.getTaskId());
}
ReActAgent agent = buildReActAgent();
agentCache.put(options.getTaskId(), agent);
return agent.streamEvents(requestMessages) // fine-grained, full lifecycle
.doFinally(signal -> agentCache.remove(options.getTaskId()));
}
```
and optionally keep a backward-compatible `stream(...)` that adapts `AgentEvent` → `Event` for callers still on the v1 API.
### Environment
- agentscope-java (agentscope-examples/boba-tea-shop A2A setup)
- ReActAgent `streamEvents` available since 2.0.0; `stream(...)` deprecated for removal.
### Reproduction (conceptual)
1. Implement an `AgentRunner` extending `BaseReActAgentRunner`.
2. Subscribe to `runner.stream(msgs, options)`.
3. Observe that emitted elements are `io.agentscope.core.agent.Event`, never `io.agentscope.core.event.AgentEvent` — so lifecycle/HITL events are unavailable to the A2A executor.
貢獻指南
評估
這個 Issue 還沒有評估資料。