While Python remains popular for model training and research, the requirements for serving and orchestrating AI agents align closely with Go’s strengths: low latency, high concurrency, and type safety.
Transitioning from a prototype to a production agent introduces engineering challenges that Golang can handle exceptionally well. Go’s static typing eliminates runtime errors when parsing structured LLM outputs. Its lightweight goroutines, which start with just a few kilobytes of stack memory, allow agents to handle thousands of concurrent tool executions without the overhead of heavy thread management.
In recent years, Go’s adoption for cloud-native microservices has surged: it showed the fourth-highest promise for languages, and maintained a 93% satisfaction rate. Google’s Agent Development Kit, or ADK, bridges the gap between these architectural advantages and generative AI.
In this guide, I’ll walk through scaffolding a new project and deploying it as a secure microservice on Google Cloud.
Get Started with the Agent Starter Pack
The good news is you don’t need to start from scratch. The Agent Starter Pack is a CLI tool that scaffolds a production-ready folder structure, including CI/CD pipelines, infrastructure configuration, and boilerplate code.
To get started, just run the create command with uvx:
uvx agent-starter-pack create
The CLI guides you through an interactive setup. For this project, I selected:
- Project Name: my-first-go-agent
- Template: Option 6 (Go ADK, Simple ReAct agent)
- CI/CD: Option 3 (GitHub Actions)
- Region: us-central1
The tool automatically authenticates with Google Cloud, enables the necessary Vertex AI APIs, and configures your local environment. Once you see the green Success! message, you’re good to go.
Web User Interface
One of the most convenient features of the ADK is the ability to visually debug your agent before deploying it. By running the command make install && make playground, you launch a local development server with a built-in UI. Yes, it has a chat window, but it goes way beyond that by tracing events, tool calls, and more.
In the screenshot below, I’m interacting with the newly created agent. The agent is configured with a ReAct (Reasoning and Acting) pattern — a framework introduced by Yao et al. in 2022 that has become foundational in agentic AI. The ReAct pattern’s continuous loop of “Thought,” “Action,” and “Observation” enhances problem-solving and interpretability, making the agent’s decision-making process transparent. It recognized the intent, invoked the get_weather tool, and returned the structured data (“It’s sunny and 72°F”).
Understanding the Code
Now that we’ve seen the agent in action, let’s look at the Go code that makes this work. The logic lives in agent/agent.go. This file handles tool definitions, model configuration, and initialization.
The ADK uses standard Go structs to define how the Large Language Model (LLM) interacts with your code. For example, to define the input parameters for our weather tool, we simply define a struct with json and jsonschema tags:
type GetWeatherArgs struct {
City string `json:"city" jsonschema:"City name to get weather for"`
}
GetWeatherResult defines the structure of the data returned to the agent after the tool executes.
type GetWeatherResult struct {
Weather string `json:"weather"`
}
GetWeather is a standard Golang function that accepts tool.Context and the arguments struct, performing the business logic and returning the result struct.
func GetWeather(_ tool.Context, args GetWeatherArgs) (GetWeatherResult, error) {
return GetWeatherResult{
Weather: "It's sunny and 72°F in " + args.City,
}, nil
}
The NewRootAgent function is responsible for assembling and returning the agent.Agent instance that the application launcher requires. It begins by initializing the model configuration, creating a gemini-2.5-flash model instance backed by genai.BackendVertexAI.
Next, it bridges the gap between Go code and the LLM by wrapping the local GetWeather function into a [functiontool](https://pkg.go.dev/google.golang.org/adk/tool/functiontool). This step registers the tool with the name get\_weather and provides the necessary description for the model’s context. Finally, it constructs the agent using llmagent.New, which combines the initialized Gemini model, the system instructions that define the agent’s behavior, and the slice of available tools into a single unit.
The NewRootAgent looks like this (with some error-handling removed):
func NewRootAgent(ctx context.Context) (agent.Agent, error) {
model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{
Backend: genai.BackendVertexAI,
})
weatherTool, err := functiontool.New(functiontool.Config{
Name: "get_weather",
Description: "Get the current weather for a city.",
}, GetWeather)
rootAgent, err := llmagent.New(llmagent.Config{
Name: "my-first-go-agent",
Model: model,
Description: "A helpful AI assistant.",
Instruction: "You are a helpful AI assistant designed to provide accurate and useful information.",
Tools: []tool.Tool{weatherTool},
})
Testing
The project contains both unit tests for internal logic, and end-to-end tests for server integration.
In agent/agent\_test.go, the GetWeather function is called with a suite of test cases, and verifies that the output string matches its expectations.
func TestGetWeather(t *testing.T) {
// tests struct initialized with "San Francisco" and "New York"
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Pass nil for tool.Context since GetWeather doesn't use it
result, err := GetWeather(nil, GetWeatherArgs{City: tt.city})
if err != nil {
t.Fatalf("GetWeather() error = %v", err)
}
if !strings.Contains(result.Weather, tt.wantCity) {
t.Errorf("GetWeather() = %v, want city %v in response", result.Weather, tt.wantCity)
}
})
}
}
The end-to-end tests verify that the agent works correctly when running as a server, specifically checking that A2A or Agent-to-Agent protocol support is working correctly. The E2E tests start a real instance of the server, sending HTTP requests to it, and check the responses. Here’s a snippet from e2e/integration/server\_e2e\_test.go:
func TestA2AMessageSend(t *testing.T) {
if testing.Short() { t.Skip("Skipping E2E test in short mode") }
// Start server (local variable to avoid race conditions)
t.Log("Starting server process")
serverProcess := startServer(t)
defer stopServer(t, serverProcess)
if !waitForServer(t, 90*time.Second) {
t.Fatal("Server failed to start")
}
t.Log("Server process started")
You can run all tests with make test:
make test
go test -v ./agent/... ./e2e/...
=== RUN TestGetWeather
=== RUN TestGetWeather/San_Francisco
=== RUN TestGetWeather/New_York
--- PASS: TestGetWeather (0.00s)
--- PASS: TestGetWeather/San_Francisco (0.00s)
--- PASS: TestGetWeather/New_York (0.00s)
PASS
ok my-first-go-agent/agent 0.218s
Deployment
The make deploy command automatically builds your application from source using Google Cloud Buildpacks, triggered by the --source . flag. It deploys this image to Cloud Run with several production-optimized flags: --memory “4Gi” to provide ample RAM for LLM operations, and --no-cpu-throttling to ensure the CPU remains allocated 24/7. This configuration is particularly valuable for Go applications.
To ensure your agent runs securely, the command is enabled with a strict configuration. It uses --no-allow-unauthenticated to block all public access by default, requiring Identity and Access Management (IAM) authentication for any requests. It also injects environment variables via --update-env-vars, including the use of Vertex AI GOOGLE\_GENAI\_USE\_VERTEXAI=True. After running the command, I have a service URL!
If you want to view the deployed web UI, I recommend deploying with make deploy IAP=true. This will handle the steps to enable IAP for Cloud Run. You will also need to provide access to users within your organization following the instructions in the documentation.
With IAP enabled, I can now view the web UI or the deployed Agent Card. This card serves as your agent’s standard interface, allowing it to be dynamically discovered by other agents, orchestrators, or human-facing UI:
What’s next?
To continue your journey building production AI agents in Golang:
- ADK Documentation: Complete guides on advanced patterns, multi-agent orchestration, and memory systems
- Agent Starter Pack: Explore templates, including multi-agent systems and complex architectures
- Cloud Run Documentation: Deep dives on performance optimization, scaling strategies, and security best practices
- Go Concurrency Patterns: Understanding goroutines and channels will help you build more efficient agent tooling
- Vertex AI Agent Engine: For managed agent infrastructure with built-in orchestration and tooling
As you scale from one agent to many, the engineering decisions we’ve discussed here compound in value. Go’s concurrency model and Cloud Run’s autoscaling are both necessary ingredients. Share what you’re building with me on LinkedIn, X, or Bluesky!






Top comments (0)