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 Developers Blog
Search
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
JUNE 30, 2026
Toni Klopfenstein
Developer Relations Engineer
ADK Developer Relations
Sampath Kumar Maddula
Developer Programs Engineer
Share
ADK for Go 2.0: build agent workflows as a graph<br>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.<br>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.<br>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 graph-based workflow engine . Alongside it come human-in-the-loop (HITL) as a built-in primitive, dynamic orchestration written in plain Go , LLM agent modes , and a unified node runtime that brings all of this together — single agents and full graphs now run on the same execution model.<br>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.
Why a graph?<br>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.<br>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, pause 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)<br>suffix := workflow.NewFunctionNode("suffix", suffixFn, cfg)
edges := workflow.Chain(workflow.Start, upper, suffix)
wf, _ := workflowagent.New(workflowagent.Config{<br>Name: "simple_sequence_workflow",<br>Edges: edges,<br>})
Go
Copied
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.
The building blocks<br>Nodes for everything<br>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:
Function nodes wrap a plain typed Go function. Generics infer the input/output schemas for you:
workflow.NewFunctionNode("classify",<br>func(ctx agent.Context, in string) (Category, error) { ... }, cfg)
Go
Copied
Emitting function nodes are function nodes that also get an emit callback, so a single function can stream events or pause for a human without dropping down to a dynamic node:
workflow.NewEmittingFunctionNode("progress",<br>func(ctx agent.Context, in Job, emit func(*session.Event) error) (Result, error) { ... }, cfg)
Go
Copied
Agent nodes drop any agent.Agent (like an LlmAgent) into the graph.<br>Tool nodes turn a tool.Tool into a graph step.<br>Join nodes are fan-in barriers: they wait for all predecessors and hand you a map of their outputs.<br>Dynamic nodes let you orchestrate in code (more on this below).<br>Workflow nodes embed an entire sub-workflow as a single node — graphs compose.<br>Parallel workers run a node concurrently across every item in a list and aggregate the results.<br>State-bound nodes (NewFunctionNodeFromState) pull selected session-state values straight into a typed Params struct via state:"" tags — no manual state plumbing.
Edges, routing, and the shapes you need<br>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()<br>b.AddRoutes(router, map[string]workflow.Node{<br>"question": answerNode,<br>"statement": commentNode,<br>"exclamation": reactNode,<br>})<br>b.AddFanOut(planner, researchA, researchB, researchC) // parallel branches<br>b.AddFanIn(join, researchA, researchB, researchC) // gather results
Go
Copied
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...