Build reliable multi-agent applications with ADK Go 2.0

atkrad1 pts0 comments

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

Facebook

Twitter

LinkedIn

Mail

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...

agent workflow nodes graph node edges

Related Articles