Go April 8, 2026

Google's ADK for Go — Build AI Agents in Pure Go

Google just released ADK for Go — an open-source toolkit to build AI agents with Gemini, custom tools, and multi-agent workflows. Here's how it works.

Google just dropped something Go developers have been waiting for — the Agent Development Kit (ADK) for Go. It’s an open-source, code-first toolkit to build AI agents. No Python. No Node. Just Go.

If you’ve been watching the AI agent space, you know one painful truth — almost everything is Python-first. LangChain, LlamaIndex, CrewAI — all Python. Go developers had to either switch languages or hand-roll their own SDK wrappers. ADK for Go finally fixes that.

Let me walk you through what it is, how it works, and show you a working example.

What Is ADK for Go?

ADK is Google’s framework for building, evaluating, and deploying AI agents. Until now, if you wanted a proper Golang LLM framework, your options were to roll your own wrappers or switch to Python. ADK Go changes that — it’s a first-class Go SDK with the same concepts as the Python version, but it feels natural in Go.

Think of it like this — if you’ve used Gin for HTTP servers, ADK is that, but for AI agents. Small API surface. Idiomatic Go. Gets out of your way.

Here’s what you get out of the box:

  • LLM agents — wire up Gemini, Claude, or Ollama with a few lines
  • Tools — give your agent the ability to call functions, search the web, or hit your own APIs
  • Workflow agents — sequential, parallel, and loop agents for multi-step tasks
  • Session and memory management — built-in services for state
  • Runners and launchers — run agents from CLI, a web UI, or your own server
  • A2A protocol — agent-to-agent communication for multi-agent systems

And because this is Go, everything is strongly typed. No more guessing what a dict is supposed to look like.

Why Build AI Agents in Golang?

Here’s the thing. Most AI tooling was built in Python because the researchers who trained the models worked in Python. But when you’re shipping agents to production — handling thousands of concurrent sessions, managing memory, deploying to Cloud Run — Go is a better fit.

Small binaries. No runtime dependencies. Built-in concurrency. Fast cold starts. All the stuff that makes Go great for backend services also makes it great for running agents.

ADK for Go means you can now build AI agents in Golang alongside the rest of your backend. No more Python sidecar services just to talk to an LLM.

Core Concepts

Before we write code, let’s understand the pieces:

ConceptWhat It Does
ModelThe LLM powering the agent (Gemini, Claude, Ollama, etc.)
AgentThe brain — takes user input, decides what to do
ToolA function the agent can call (search, database, custom logic)
RunnerRuns the agent, manages sessions and events
SessionHolds conversation state and history

An agent is basically a model + instructions + tools. You tell it who it is, what it should do, and what abilities it has. The model figures out the rest.

Prerequisites

Before we start, make sure you’ve got:

  • Go 1.24.4 or later — ADK Go uses some newer language features. Check with go version.
  • A Gemini API key — grab a free one from Google AI Studio.
  • Basic Go familiarity — you should be comfortable with structs, interfaces, and go mod.

That’s it. No Docker, no Python runtime, no extra tooling.

Building Your First AI Agent with ADK for Go

Let’s build a simple assistant. First, create a new Go module and install the packages:

go mod init my-agent
go get google.golang.org/adk
go get google.golang.org/genai

Then export your Gemini API key so the agent can talk to the model:

export GOOGLE_API_KEY="your-key-here"

Now here’s a minimal agent that uses Gemini and the built-in Google Search tool:

package main

import (
	"context"
	"log"
	"os"

	"google.golang.org/genai"

	"google.golang.org/adk/agent/llmagent"
	"google.golang.org/adk/model/gemini"
	"google.golang.org/adk/tool"
	"google.golang.org/adk/tool/geminitool"
)

func main() {
	ctx := context.Background()

	model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{
		APIKey: os.Getenv("GOOGLE_API_KEY"),
	})
	if err != nil {
		log.Fatalf("model init failed: %v", err)
	}

	weatherAgent, err := llmagent.New(llmagent.Config{
		Name:        "weather_agent",
		Description: "Answers weather questions using Google Search.",
		Model:       model,
		Instruction: "You answer weather questions. Use the search tool when needed.",
		Tools: []tool.Tool{
			geminitool.GoogleSearch{},
		},
	})
	if err != nil {
		log.Fatalf("agent init failed: %v", err)
	}

	log.Printf("Agent ready: %s", weatherAgent.Name())
}

Four things happen here. You create a Gemini model. You create an LLM agent with a name, description, and instructions. You pass in the Google Search tool. That’s it — your agent can now answer weather questions by searching the web.

But just creating an agent isn’t useful. You need to run it.

Running Your Go AI Agent Locally

ADK gives you two ways to run agents — the Launcher (for CLI/web UI) and the Runner (for production).

For quick testing, the Launcher is perfect. But there’s a catch — to get the web UI working you need to wire up a few services. Here’s the full setup:

package main

import (
	"context"
	"log"
	"os"

	"google.golang.org/genai"

	"google.golang.org/adk/agent"
	"google.golang.org/adk/agent/llmagent"
	"google.golang.org/adk/artifact"
	"google.golang.org/adk/cmd/launcher"
	"google.golang.org/adk/cmd/launcher/full"
	"google.golang.org/adk/memory"
	"google.golang.org/adk/model/gemini"
	"google.golang.org/adk/session"
)

func main() {
	ctx := context.Background()

	model, _ := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{
		APIKey: os.Getenv("GOOGLE_API_KEY"),
	})

	myAgent, _ := llmagent.New(llmagent.Config{
		Name:        "assistant",
		Model:       model,
		Description: "A helpful assistant",
		Instruction: "You are a helpful assistant.",
	})

	config := &launcher.Config{
		AgentLoader:     agent.NewSingleLoader(myAgent),
		SessionService:  session.InMemoryService(),
		ArtifactService: artifact.InMemoryService(),
		MemoryService:   memory.InMemoryService(),
	}

	l := full.NewLauncher()
	if err := l.Execute(ctx, config, os.Args[1:]); err != nil {
		log.Fatalf("run failed: %v", err)
	}
}

Three services matter here:

  • SessionService — holds conversation history between turns
  • ArtifactService — stores any files or data your tools produce
  • MemoryService — stores long-term memory across sessions

ADK ships with InMemoryService() variants for all three — perfect for local dev. For production you’d swap these for Firestore, Cloud SQL, or your own implementation.

Now you can run your agent in different modes:

go run main.go console              # interactive terminal
go run main.go web api webui        # REST API + Web UI at localhost:8080

The web command takes sublaunchersapi starts the REST API server and webui starts the chat interface. Run both together and you get a full dev playground at http://localhost:8080 with chat, tool call inspection, and session state viewer. All without writing any frontend code.

Custom Tools and Gemini Function Calling in Go

The built-in tools are nice, but the real power is giving your agent access to your own functions. This is where Gemini function calling in Go actually shines — ADK handles all the schema plumbing for you. Let’s add a custom weather tool:

package main

import (
	"fmt"

	"google.golang.org/adk/tool"
	"google.golang.org/adk/tool/functiontool"
)

type WeatherInput struct {
	City    string `json:"city" jsonschema:"City name"`
	Country string `json:"country,omitempty" jsonschema:"Country code"`
}

type WeatherOutput struct {
	Temperature float64 `json:"temperature"`
	Condition   string  `json:"condition"`
}

func getWeather(ctx tool.Context, in WeatherInput) (WeatherOutput, error) {
	// Call your real weather API here
	return WeatherOutput{
		Temperature: 32.5,
		Condition:   fmt.Sprintf("Hot and humid in %s", in.City),
	}, nil
}

func newWeatherTool() (tool.Tool, error) {
	return functiontool.New(functiontool.Config{
		Name:        "get_weather",
		Description: "Gets current weather for a city",
	}, getWeather)
}

Look at what’s happening here. You define plain Go structs for input and output. You write a regular Go function. functiontool.New takes care of the rest — it reads your struct tags, builds a JSON schema, and registers the tool so the LLM knows how to call it.

No decorators. No magic. Just structs and functions. This is why Go feels so good for this — the type system does the heavy lifting.

To use the tool, pass it to your agent:

weatherTool, _ := newWeatherTool()

myAgent, _ := llmagent.New(llmagent.Config{
    Name:        "weather_agent",
    Model:       model,
    Description: "Weather assistant",
    Instruction: "Answer weather questions using the get_weather tool.",
    Tools:       []tool.Tool{weatherTool},
})

Now when a user asks “what’s the weather in Chennai?”, the agent will call your getWeather function with {city: "Chennai"} and use the result to answer.

One gotcha I hit — save this for later. Gemini doesn’t let you mix built-in tools like GoogleSearch{} and custom function tools in the same agent. You’ll get this error: Built-in tools ({google_search}) and Function Calling cannot be combined in the same request. Please choose one to continue. Pick one or the other per agent. If you need both, split them across two agents and orchestrate with a workflow.

Seeing It Run

Time to fire it up. Make sure your GOOGLE_API_KEY is set, then start the web UI:

export GOOGLE_API_KEY="your-key-here"
go run main.go web api webui

Open http://localhost:8080 in your browser. You’ll land on a full chat playground for your agent. Ask it What's the weather in Chennai? and then follow up with Compare the weather in Chennai and Mumbai. Here’s what came back for me:

ADK Go agent web UI chat example showing the weather_agent in action. The user asked "What's the weather in Chennai?" — the agent called the get_weather tool and replied "It is Hot and humid with a temperature of 33 degrees Celsius and 72% humidity in Chennai." A follow-up asking to compare Chennai and Mumbai triggered two parallel get_weather tool calls and a combined answer.

Notice a few things:

  • Tool calls are first-class — each get_weather call shows up as a badge in the chat, with a loading state and a checkmark when it’s done
  • Parallel tool calls just work — when I asked to compare Chennai and Mumbai, ADK fired both get_weather calls at the same time. Zero extra code from me.
  • Session state is tracked — the Trace, State, Artifacts, Sessions, and Eval tabs on the left give you everything you need to debug what the agent is actually doing

If you’d rather stay in the terminal, use console mode instead — same agent, just no browser:

go run main.go console

Type your question at the User -> prompt and the agent replies inline:

ADK Go agent console mode output. The user typed "Whats the weather in Chennai?" and the agent logged "[tool] get_weather called for city='Chennai' country='India'" before replying "The weather in Chennai is Hot and humid with a temperature of 33 degrees Celsius and 72% humidity."

That [tool] get_weather called for city="Chennai" country="India" line is from the log.Printf inside the Go function. The agent read the user’s message, figured out it needed the weather tool, extracted both the city and the country from the prompt, and called my function with the right arguments. No parsing, no intent classification, no regex — the LLM just does it.

Multi-Agent Workflows

Here’s where ADK gets interesting. For complex tasks, you don’t use one big agent — you chain small specialized agents together. It’s like a restaurant kitchen. One cook handles starters, another the main course, another desserts. Each does one thing well.

ADK gives you three workflow types:

  • Sequential — run agents in order (step 1 → step 2 → step 3)
  • Parallel — run agents at the same time (fan-out search across sources)
  • Loop — keep running until a condition is met (iterative refinement)

Let’s do something real. Say you’re drowning in GitHub issues and want to auto-triage them. You need three agents working in order:

  1. issue_reader — pulls the issue title and body from GitHub
  2. triage_bot — an LLM agent that categorizes the issue (bug, feature, question) and drafts a reply
  3. slack_notifier — posts a summary to your team’s Slack channel

Each agent does one thing. Chain them with a sequentialagent and you’ve got a ticket triage workflow:

import (
	"google.golang.org/adk/agent"
	"google.golang.org/adk/agent/llmagent"
	"google.golang.org/adk/agent/workflowagents/sequentialagent"
	"google.golang.org/adk/tool"
)

// Agent 1: fetch the issue (custom Run func that hits GitHub API)
issueReader, _ := agent.New(agent.Config{
    Name:        "issue_reader",
    Description: "Reads an incoming GitHub issue",
    Run:         fetchIssueFromGitHub, // your custom run function
})

// Agent 2: LLM categorizes the issue and drafts a reply
triageBot, _ := llmagent.New(llmagent.Config{
    Name:        "triage_bot",
    Model:       model,
    Description: "Categorizes issues and drafts replies",
    Instruction: "Categorize the issue as bug, feature, or question. Then draft a short, friendly reply acknowledging the report.",
    Tools:       []tool.Tool{draftReplyTool},
})

// Agent 3: post the summary to Slack (custom Run func)
slackNotifier, _ := agent.New(agent.Config{
    Name:        "slack_notifier",
    Description: "Posts the triage summary to Slack",
    Run:         postToSlack, // your custom run function
})

// Chain them together
triage, err := sequentialagent.New(sequentialagent.Config{
    AgentConfig: agent.Config{
        Name:        "issue_triage",
        Description: "Read → categorize → notify",
        SubAgents:   []agent.Agent{issueReader, triageBot, slackNotifier},
    },
})

Each sub-agent runs in order. The output of one feeds the next via session state — so triage_bot sees the issue that issue_reader fetched, and slack_notifier sees the draft reply that triage_bot produced. Swap sequentialagent for parallelagent and they run at the same time. Swap it for loopagent and it keeps looping until a sub-agent sets Escalate = true.

This pattern scales to anything — PR reviewers, invoice processors, RAG pipelines, content moderation bots. Build small focused agents, chain them, ship it.

Official docs: adk.dev/get-started/go · Source: github.com/google/adk-go.

Toggle theme (T)