cd /news/ai-agents/build-reliable-multi-agent-applicati… · home topics ai-agents article
[ARTICLE · art-45164] src=developers.googleblog.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Build reliable multi-agent applications with ADK Go 2.0. Discover our new graph-based workflow engine, built-in human-in-the-loop, and dynamic orchestration

Google released ADK Go 2.0, a major update to its Agent Development Kit for Go, introducing a graph-based workflow engine for building reliable multi-agent applications. The new version features built-in human-in-the-loop support, dynamic orchestration, and a scheduler that handles concurrent execution, state persistence, and pause-resume across restarts.

read7 min views1 publishedJun 30, 2026
Build reliable multi-agent applications with ADK Go 2.0. Discover our new graph-based workflow engine, built-in human-in-the-loop, and dynamic orchestration
Image: Developers (auto-discovered)

Building real-world agent applications is rarely as simple as sending a single prompt. Production agents must classify, branch, fan out, ask a human to approve something, retry on failure, and loop until done. Expressing that complex orchestration as ad-hoc control flow gets brittle fast.

Since its 1.0 release, Agent Development Kit (ADK) for Go has helped Go developers build production agents with a clean, idiomatic API — strong typing, iter.Seq2

event streams, and a runtime that fits naturally into existing Go services. That foundation has been a real success, and it's exactly what made the next step possible.

Today we're excited to share ADK for Go 2.0. The headline is a brand-new, first-class way to compose multi-agent applications: a

If you've followed Python ADK 2.0, this will feel familiar: it's the same graph-first direction, designed from the ground up to feel like Go.

Real agent applications are rarely a single prompt. They classify, branch, fan out to specialists, gather results, ask a human to approve something, retry on failure, and loop until done. Expressing that as ad-hoc control flow gets brittle fast.

ADK 2.0 lets you describe the shape of your application as a graph of nodes connected by edges, and hands execution to a scheduler that knows how to run it concurrently, persist its state, for a human, and resume later — even across process restarts. Here is how simple it is to chain nodes together:

import "google.golang.org/adk/v2/workflow"

upper  := workflow.NewFunctionNode("upper",  upperFn,  cfg)
suffix := workflow.NewFunctionNode("suffix", suffixFn, cfg)

edges := workflow.Chain(workflow.Start, upper, suffix)

wf, _ := workflowagent.New(workflowagent.Config{
    Name:  "simple_sequence_workflow",
    Edges: edges,
})

That wf

is just an agent.Agent

. It runs in the same runner, launcher, and console you already use — no special harness, no new server. A graph is an agent.

A node is any unit of work that implements the Node interface. You rarely write that interface by hand — ADK ships typed node constructors for the common cases:

workflow.NewFunctionNode("classify",
    func(ctx agent.Context, in string) (Category, error) { ... }, cfg)

emit

callback, so a single function can

workflow.NewEmittingFunctionNode("progress",
    func(ctx agent.Context, in Job, emit func(*session.Event) error) (Result, error) { ... }, cfg)

agent.Agent

(like an LlmAgent

) into the graph.tool.Tool

into a graph step.NewFunctionNodeFromState

) pull selected session-state values straight into a typed Params struct via state:"<key>"

tags — no manual state plumbing.Edges connect nodes, and they can carry routing conditions. A node emits a routing value; matching edges fire. That single idea gives you every control-flow shape you need:

b := workflow.NewEdgeBuilder()
b.AddRoutes(router, map[string]workflow.Node{
    "question":    answerNode,
    "statement":   commentNode,
    "exclamation": reactNode,
})
b.AddFanOut(planner, researchA, researchB, researchC) // parallel branches
b.AddFanIn(join, researchA, researchB, researchC)       // gather results

Sequential chains, conditional routers, fan-out/fan-in, nested sub-graphs, and even loops (a completed node can be re-triggered, so cycles are first-class) — all from edges and routes. Standard routes come in StringRoute

, IntRoute

, BoolRoute

, MultiRoute

, and a Default

that fires when nothing else matches. For deeper configuration, leverage the Route interface.

One of the most useful patterns is using a model as the brain of a router. An LlmAgent classifies the user's message; a trivial function emits the matching route; the graph dispatches to the right handler:

User -> What time is it?    Agent -> question     answering question...
User -> Hello world!        Agent -> exclamation  reacting to exclamation...
User -> The sky is blue.    Agent -> statement    commenting on statement...

The model makes the decision; the graph makes it reliable, observable, and resumable. (See examples/workflow/routing/llm/.)

Sometimes the execution order isn't known until runtime: it depends on data, on a loop count, on what the model just said. For that, ADK 2.0 gives you dynamic nodes, where the orchestration body is ordinary Go code that calls RunNode(...)

for each child:

greeter := workflow.NewDynamicNode("greeter_workflow",
    func(nc agent.Context, in string, emit func(*session.Event) error) (string, error) {
        return workflow.RunNode[string](nc, greeterNode, in)
    },
    workflow.NodeConfig{},
)

Loops, conditionals, accumulation, fan-out across a dynamic list — all expressed with the Go you already know. Options like WithRunID

, WithUseSubBranch

, WithUseAsOutput

, and WithIsolationScope

give you precise control over child identity, history isolation, and output delegation. This is the Go counterpart to Python ADK's dynamic graphs.

Production agents often need a human to approve, correct, or supply something mid-run. In ADK 2.0, any node can the graph and ask a human a question — and the workflow durably waits for the answer:

event := workflow.NewRequestInputEvent(ctx, session.RequestInput{
    InterruptID:    "approve_refund",
    Message:        "Approve a $200 refund? (yes/no)",
    ResponseSchema: schema,
})
// yield the event; the node moves to "waiting"

When the human replies on a later turn, the workflow resumes. You choose how:

ctx.ResumedInput(...)

.And resume is durable. The run state lives in the session, and ADK can even reconstruct a d workflow by scanning session history — so a workflow can resume after a process restart, or even across different runtimes, because the interrupt format is shared with Python ADK. Responses are validated against a schema, resume is idempotent, and you get clear errors (ErrInvalidResumeResponse

, ErrNothingToResume

) when something doesn't line up.

Both the console launcher and the Web UI understands HITL out of the box, surfacing both tool-confirmation prompts and workflow input requests.

Every node can carry a retry policy with exponential backoff and jitter — no external dependency required:

cfg := workflow.NodeConfig{ RetryConfig: workflow.DefaultRetryConfig() }
// 5 attempts, 1s initial delay, 60s cap, 2x backoff, full jitter

Add a per-node Timeout

, cap graph-wide concurrency with WithMaxConcurrency(n)

, and isolate parallel branches so one branch's chatter never leaks into another's LLM prompt history. The scheduler handles the goroutines, channels, backpressure, and cancellation for you.

ADK 2.0 introduces modes for LLM agents — Chat

, Task

, and SingleTurn

— so a coordinator can chat with the user while sub-agents quietly complete tasks or run single-shot. The right helper tools (finish_task

, single_turn

, task

) are installed automatically based on each agent's role.

Under the hood, the runner now drives a plain LlmAgent

through the same node runtime that powers workflows. The payoff: single-agent apps and full graphs share one execution model, and human-in-the-loop now works for a plain LLM agent too — not just inside a workflow.

We also smoothed the programming model: ToolContext

and CallbackContext

are now a single unified to agent.Context

— one type to learn, whether you're writing a tool, a callback, or a graph node — and node/agent execution shows up in one consistent telemetry span tree, so you can see exactly what your graph did.

ADK 2.0 is highly additive — the entire workflow engine is new packages you opt into. There are a few new and breaking changes that come with unifying the runtime; each has a simple, mechanical fix:

agent.Context

agent.InvocationContext

to agent.Context

(it embeds InvocationContext

, so every method you used still works):

// before:  func(ctx agent.InvocationContext, in string) (string, error)
// after:   func(ctx agent.Context,           in string) (string, error)

ToolContext

, CallbackContext

are gone – tools, callbacks, and workflow nodes all receive agent.Context

directly. If you mocked a context in tests, agent/context_mock.go

is retained; use StrictContextMock

from that file as your test double.InvocationContext

IsolationScope()

and ResumedInput(id string)

. Most code embeds the provided implementation and gets these for free.IsolationScope

, Output

,Routes

,RequestedInput

) and a metadata field (NodeInfo

). If you assert on exact session.Event

equality in tests, expect the new fields; custom session stores should persist them.llmagent.New

task

-mode agents can't be used as static graph nodes.NewEvent(ctx context.Context, invocationID string)

. Migrate call sites by passing the context.Context

already in scope as the first argument.That's the whole list. Public signatures for runner.Run/RunLive

, agenttool

, and the llmagent callbacks are unchanged. For step-by-step before/after instructions, see the ADK Go 2.0 migration guide.

The fastest way to get a feel for ADK 2.0 is the new workflow examples:

go run ./examples/workflow/basic/
go run ./examples/workflow/routing/llm/      # LLM-as-router
go run ./examples/workflow/dynamic/hitl/     # dynamic + human-in-the-loop
go run ./examples/workflow/hitl_rerun/       # HITL with re-entry resume
go run ./examples/workflow/complex/          # a larger, multi-shape graph

ADK 1.0 proved that building serious agents in Go could be clean and productive. ADK 2.0 takes the next step: compose those agents into reliable, observable, resumable workflows — as a graph, in idiomatic Go, with humans in the loop when it matters.

We can't wait to see what you build.

— The ADK for Go team

── more in #ai-agents 4 stories · sorted by recency
── more on @google 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/build-reliable-multi…] indexed:0 read:7min 2026-06-30 ·