Agent Orchestration

Orchestration is the single question of how you invoke a unit of work — a sub-agent, a system fetch, or an OOB LLM — and where its result goes. Every mechanism here writes its result to governed State under {name}:result (or {name}:error), so coordination is reactive and uniform: a Flow step completes on it via Guard::resolved(name), a watcher fires on it, or the next agent reads it — regardless of who produced it.

Mode — how an agent is invoked

use gemini_adk_fluent_rs::prelude::*;   // AgentMode, call_agent, Resolver

// Call — synchronous; the caller awaits. Use only for fast dependencies.
let verdict = call_agent("availability", agent, &state).await?;   // → availability:result

// Dispatch — fire-and-forget; the conversation does not wait.
// Background — model-aware; runs detached, result delivered back to the model.
ModeSync?Lowers to
Callsync — caller awaitsagent run inline, result → State
Dispatchasync, fire-and-forgetBackgroundAgentDispatcher::dispatch
Backgroundasync, model-awarean agent-tool marked ToolExecutionMode::Background

Resolver — a named async value source

A Resolver generalizes call from "a sub-agent" to any async source whose inputs come from State. It is the async sibling of the deterministic Recognizer:

use gemini_adk_rs::Resolver;

// A sub-agent (its String output becomes the result):
Resolver::agent("availability", availability_agent).resolve(&state).await?;

// Any async system — a tool call, HTTP fetch, or MCP request — bound from State:
Resolver::fetch("availability", |s: State| async move {
    let slot = s.get::<String>("slot").unwrap_or_default();
    Ok(serde_json::json!({ "open": slot == "afternoon" }))
}).resolve(&state).await?;          // or .dispatch(state) to run detached

// A one-shot OOB LLM over a {key}-interpolated prompt:
Resolver::llm("summary", flash_llm, "Summarize the {topic} issue").resolve(&state).await?;

All three write {name}:result and record provenance under state_meta:{name}:result (source: agent | fetch | llm), readable with provenance(&state, "name:result").

Flow-driven orchestration

A governed Flow drives orchestration in-session: a step's on_enter runs a resolver/agent the moment it activates, and an Extract record can dispatch a downstream agent on_complete:

Live::builder()
    .govern(booking_flow)
    .on_enter("check", availability_agent, AgentMode::Call)   // result → check:result
    .extract_record(
        Extract::record("triage")
            .field("intent", Recognizer::one_of(["refund", "status"]))
            .on_complete(router_agent, AgentMode::Dispatch)    // result → triage:result
            .build(),
    )
    .connect_from_env().await?;

Because every result lands in State under the same convention, the three lenses — extraction, orchestration, and flow — compose multiplicatively: extraction fills slots, a step orchestrates a sub-agent or fetch, and guards gate on either.

See also