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.
| Mode | Sync? | Lowers to |
|---|---|---|
Call | sync — caller awaits | agent run inline, result → State |
Dispatch | async, fire-and-forget | BackgroundAgentDispatcher::dispatch |
Background | async, model-aware | an 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
- Extraction Pipeline —
Recognizer/Resolverfield sources - Governed Flows —
on_enter,ground,Guard::resolved - Text Agent Combinators — building the agents you orchestrate
- cookbook 39 — booking (Flow × Extract × Orchestration)