google / google/adk-python

LoopAgent: surface the current iteration index in the event stream

Abierto
#6,266 2 comentarios 0 reacciones 2 asignados Reclamado por @i-yliu Ver en GitHub
core needs review
Lenguaje dominante
Python
Estrellas
21.5k
Forks
4k
Merge medio
1 d 14 h
PR fusionados (30 d)
37

Descripción

### Context

We maintain the **AG-UI ↔ ADK middleware** (`ag-ui-adk`, part of [ag-ui-protocol/ag-ui](https://github.com/ag-ui-protocol/ag-ui)). AG-UI is a protocol between agents and frontends; one of its event categories is `STEP_STARTED` / `STEP_FINISHED`, which bracket the phases/nodes of a run (the spec recommends `stepName = node name`).

To emit these from ADK workflows (`SequentialAgent` / `ParallelAgent` / `LoopAgent` / the 2.0 `Workflow` graph), the middleware infers node boundaries from the `runner.run_async` event stream using each event's `author` (node name) and `branch` (`ParallelAgent` assigns a distinct `par.` per branch via `_create_branch_ctx_for_sub_agent`). This works cleanly for serial and parallel topologies.

### Problem

There is **one gap: a `ParallelAgent` nested inside a `LoopAgent`.** The concurrent branches keep the **same `author`, `branch`, and `invocation_id`** across iterations, and the flat event stream carries **no iteration-boundary signal** — so a consumer cannot tell iteration 1 from iteration 2. The events are identical except for their unique `id`. (Verified on `google-adk 2.3.0`.)

`LoopAgent(max_iterations=2, sub_agents=[ParallelAgent(sub_agents=[p, q])])` emits:

```
author='p' branch='par.p' invocation_id='e-…' ← iteration 1
author='q' branch='par.q' invocation_id='e-…'
author='p' branch='par.p' invocation_id='e-…' ← iteration 2 (indistinguishable)
author='q' branch='par.q' invocation_id='e-…'
```

As a result, per-iteration steps cannot be reconstructed for this topology.

### ADK already tracks the iteration

`LoopAgent._run_async_impl` already maintains `times_looped`, and on the resumable path it even yields a state event carrying it (`self._create_agent_state_event(ctx)`). It's simply not surfaced on the events produced *within* an iteration.

### Ask (minimal, additive)

Surface the current loop iteration in the event stream, either:

- a **read-only field** on events produced within a `LoopAgent` iteration (e.g. `Event.loop_iteration`, or in event metadata), or
- a lightweight **iteration-boundary marker** event emitted at each iteration start (generalizing the existing resumable-path state event to always fire).

This is purely additive and does not change execution behavior.

### Why not put the iteration into `branch`?

That would be convenient for consumers, but `branch` governs context/event isolation — changing it per iteration could affect how loop iterations **share state** (the whole point of a loop). So we're explicitly asking to expose the iteration **separately from** `branch`.

### Motivation

We need this to faithfully emit **all** AG-UI event types from ADK. `STEP_STARTED`/`STEP_FINISHED` is currently the one category the ADK integration can only approximate (for loop-of-parallel). See the AG-UI PR: ag-ui-protocol/ag-ui#2076.

### Related

A more general alternative — emitting explicit agent/node **lifecycle** events in the stream — is filed as the companion issue #6267, which would subsume this. This issue (#6266) is the minimal, lowest-risk subset scoped to loops.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.