diff --git a/Graph-Agents.md b/Graph-Agents.md index fe044d1..0ab5b10 100644 --- a/Graph-Agents.md +++ b/Graph-Agents.md @@ -314,6 +314,8 @@ research_topic: agent: deep-researcher # name of an existing Coyote agent prompt: "Research {{topic}}" # interpolated against state timeout: 600 # optional, seconds; default 300, 0 = no bound + max_attempts: 2 # optional; retry transient child failures (default 1) + fallback: report_unavailable # optional; route here when the child ultimately fails inputs: # optional; only when the child is a GRAPH agent max_parallel: "{{inner_width}}" # seeds the child's initial_state (see below) teammates: false # optional; peer messaging with concurrent siblings (see below) @@ -327,11 +329,30 @@ research_topic: `/agents/`. - **`prompt`:** The user message sent to the child agent. Templated against the current graph state. -- **`timeout`:** Hard wall-clock cap. If the child agent exceeds it, the - whole graph fails (no built-in fallback path on agent nodes). `0` +- **`timeout`:** Hard wall-clock cap per attempt. A timeout counts as a + transient failure: remaining `max_attempts` retries run, each with a fresh + budget; the final failure routes via `fallback` when declared, otherwise + the whole graph fails at this node. `0` disables the bound; the child runs until it returns. Required on the calling node to let a child graph's own `timeout: 0` take effect; the child's non-zero `settings.timeout` still applies. +- **`max_attempts`:** Transient-error retries with the same matcher and + semantics as `llm` nodes (see [Retries](#retries-max_attempts)). Each retry + re-spawns the child agent from scratch with the same prompt and a fresh + `timeout` budget; a concurrent-teammate identity (inbox + roster) is + preserved across attempts. Default `1` (no retries). Only set this on + children that are safe to re-run from scratch; e.g. read-only reviewers, + researchers, report writers. Leave it at `1` for side-effectful children + (agents that write code or deploy things); a child that authors files + (e.g. a probe lane writing test artifacts) is mildly side-effectful, + meaning a failed first attempt may leave partial artifacts behind. +- **`fallback`:** Failure route with the same contract as `llm` nodes: on the + final failure (retries exhausted, hard error, or structured-output + extraction failure) the node writes `Agent node failed: ` as + `{{output}}`, applies `state_updates`, and routes to `fallback` instead of + failing the graph. Without `fallback` the error propagates and the graph + fails at this node. Inside a map branch the fallback target must be + branch-local. - **`state_updates`:** Map of `state_key: "{{template}}"`. The child agent's final text is available inside this map as `{{output}}`. - **`inputs`:** Map of `child_state_key: "{{template}}"`. Each value is @@ -720,10 +741,19 @@ graph a chance to recover or report through its `fallback` route. `max_attempts` retries the LLM call **only on transient errors**. The failure message containing one of: `timed out`, `rate limit`, `429`, -`Connection reset`, `Connection refused`, or `produced no output`. Any +`Connection reset`, `Connection refused`, `produced no output`, +`broken pipe`, `connection error`, or `error sending request`. Any other error fails immediately without consuming further attempts. The default is `1` (no retries). +`agent` nodes support the same key with the same transient matcher: the +child agent is re-spawned **from scratch** on a transient failure, each +attempt with a fresh `timeout` budget (a per-attempt timeout counts as +transient). A retried teammate keeps the same identity, inbox, and roster +across attempts. See [agent](#agent) for when re-running a child is safe; +structured-output extraction failures are never retried on either node +type (they route via `fallback` instead). + ### Turn cap (`max_iterations`) `max_iterations` caps the number of model-call turns in one execution of the @@ -1191,7 +1221,7 @@ Rules for the loop: - Item errors are fail-fast and carry the chain position: `map node 'review_each_file': sub-branch [2] failed at node 'check_review' (step 2): ...`. - A `script` step that fails consults its `fallback:` (a branch-local node) - before failing the item, exactly like a main-flow script. An `llm` step's + before failing the item, exactly like a main-flow script. An `llm` or `agent` step's `max_attempts` and `fallback:` work per item the same way. ### Empty `over` @@ -1318,7 +1348,7 @@ Nodes route via three mechanisms in priority order: | Node type | Needs `next`? | Supports fan-out (`next: [...]`)? | |-------------|---------------------------------------------------------------------------------------------------|-----------------------------------| -| `agent` | **Yes** - `next` is required (unless the agent node is unreachable). Error at runtime if missing. | Yes | +| `agent` | **Yes** - `next` is required (unless the agent node is unreachable). Error at runtime if missing. Failures without `fallback` halt the graph. | Yes (success path; failure with `fallback` routes to single target) | | `script` | Either `_next` from script output OR static `next` (or `fallback` on failure). Error if neither. | Yes (when `_next` is not emitted) | | `approval` | No - routing is via `routes` and `on_other`. `next` is ignored. | No - forbidden by validator | | `input` | **Yes** - `next` is the success route. | No - forbidden by validator | @@ -1336,14 +1366,14 @@ an error. Fan-out is never allowed inside a branch. See | Node type | Success | Failure with `fallback` | Failure without `fallback` | |----------------------------|-----------------------------|-------------------------|-----------------------------------------------------------| -| `llm` | Routes via `next` | Routes via `fallback` | **Graph fails at this node** (use `fallback:` to recover) | +| `llm` / `agent` | Routes via `next` | Routes via `fallback` | **Graph fails at this node** (use `fallback:` to recover) | | `script` | Routes via `next` | Routes via `fallback` | Routes via `next`; `{{output}}` holds the error | -| `agent` / `input` | Routes via `next` | n/a (no `fallback`) | Graph fails at this node | +| `input` | Routes via `next` | n/a (no `fallback`) | Graph fails at this node | | `rag` / `map` / `approval` | Routes via configured edges | n/a | Graph fails at this node | -**Note:** LLM node failures halt the graph with +**Note:** LLM and agent node failures without a `fallback` halt the graph with a clear error message. This prevents downstream nodes from running against -garbage state when an upstream LLM call fails (HTTP 4xx/5xx, timeout, +garbage state when an upstream call fails (HTTP 4xx/5xx, timeout, structured-extraction error, etc.). ---