# ADK-Go Developer Reference Guide A practical guide for building AI agents with Google's Agent Development Kit for Go. --- ## Quick Start ### Prerequisites - Go 1.24.4 or later - Google API key (AI Studio) or Google Cloud project (Vertex AI) ### Installation ```bash # Create new project mkdir my-agent && cd my-agent go mod init example.com/my-agent # Install ADK go get google.golang.org/adk ``` ### Environment Setup ```bash # Option 1: AI Studio (API Key) export GOOGLE_API_KEY="your-api-key" # Option 2: Vertex AI (Application Default Credentials) export GOOGLE_CLOUD_PROJECT="your-project-id" export GOOGLE_CLOUD_LOCATION="us-central1" gcloud auth application-default login ``` ### Minimal Agent ```go package main import ( "context" "log" "os" "google.golang.org/adk/agent/llmagent" "google.golang.org/adk/cmd/launcher/adk" "google.golang.org/adk/cmd/launcher/full" "google.golang.org/adk/model/gemini" "google.golang.org/adk/server/restapi/services" "google.golang.org/genai" ) func main() { ctx := context.Background() // Create model model, err := gemini.NewModel(ctx, "gemini-3-flash-preview", &genai.ClientConfig{ APIKey: os.Getenv("GOOGLE_API_KEY"), }) if err != nil { log.Fatal(err) } // Create agent agent, err := llmagent.New(llmagent.Config{ Name: "assistant", Model: model, Description: "A helpful assistant", Instruction: "You are a helpful assistant. Be concise and accurate.", }) if err != nil { log.Fatal(err) } // Launch l := full.NewLauncher() cfg := &adk.Config{AgentLoader: services.NewSingleAgentLoader(agent)} if err := l.Execute(ctx, cfg, os.Args[1:]); err != nil { log.Fatal(err) } } ``` ### Running Modes ```bash # Console mode (interactive terminal) go run main.go # Web UI + API go run main.go web api webui # Opens at http://localhost:8080 # Production (API + A2A protocol) go run main.go web api a2a ``` --- ## Core Imports ```go import ( // Agents "google.golang.org/adk/agent" "google.golang.org/adk/agent/llmagent" "google.golang.org/adk/agent/sequentialagent" "google.golang.org/adk/agent/parallelagent" "google.golang.org/adk/agent/loopagent" // Models "google.golang.org/adk/model/gemini" "google.golang.org/genai" // Tools "google.golang.org/adk/tool" "google.golang.org/adk/tool/functiontool" "google.golang.org/adk/tool/geminitool" "google.golang.org/adk/tool/mcptool" // Session & Memory "google.golang.org/adk/session" "google.golang.org/adk/memory" // Launcher "google.golang.org/adk/cmd/launcher/adk" "google.golang.org/adk/cmd/launcher/full" "google.golang.org/adk/server/restapi/services" ) ``` --- ## Creating Agents ### LLM Agent Configuration ```go agent, err := llmagent.New(llmagent.Config{ // Required Name: "my_agent", Model: model, // Recommended Description: "What this agent does (used for routing)", Instruction: "System prompt with behavior guidelines", // Optional Tools: []tool.Tool{...}, // Available tools SubAgents: []agent.Agent{...}, // Agents this can delegate to OutputKey: "result", // Save output to state["result"] }) ``` ### Instruction Templates Use curly braces to inject state values: ```go llmagent.Config{ Instruction: `You are a travel assistant. User preferences: {user:preferences} Current itinerary: {itinerary} Budget remaining: {budget}`, } ``` ### Available Models ```go // Gemini models "gemini-3-flash-preview" // Fast, efficient "gemini-2.5-pro" // Most capable "gemini-3-flash-preview" // Previous generation "gemini-1.5-pro" // Long context ``` --- ## Tools ### Built-in Tools ```go import "google.golang.org/adk/tool/geminitool" agent, _ := llmagent.New(llmagent.Config{ Tools: []tool.Tool{ geminitool.GoogleSearch{}, // Web search geminitool.CodeExecution{}, // Run code in sandbox }, }) ``` ### Custom Function Tools ```go import "google.golang.org/adk/tool/functiontool" // 1. Define input/output structs with JSON tags type CalculateInput struct { Expression string `json:"expression" jsonschema:"Math expression to evaluate"` } type CalculateOutput struct { Result float64 `json:"result"` Error string `json:"error,omitempty"` } // 2. Implement the function func calculate(ctx tool.Context, input CalculateInput) CalculateOutput { // Your logic here return CalculateOutput{Result: 42} } // 3. Create the tool calcTool, err := functiontool.New( functiontool.Config{ Name: "calculate", Description: "Evaluates mathematical expressions", }, calculate, ) // 4. Add to agent agent, _ := llmagent.New(llmagent.Config{ Tools: []tool.Tool{calcTool}, }) ``` ### Schema Tags Reference ```go type Input struct { // Required field with description Query string `json:"query" jsonschema:"Search query to execute"` // Optional field (omitempty makes it optional) Limit int `json:"limit,omitempty" jsonschema:"Max results (default 10)"` // Enum values Format string `json:"format" jsonschema:"Output format" jsonschema_enum:"json,xml,csv"` } ``` ### Accessing Context in Tools ```go func myTool(ctx tool.Context, input MyInput) MyOutput { // Access session state userID := ctx.Session().State().Get("user:id") // Access artifacts artifact, err := ctx.Artifacts().Load("document.pdf") // Standard context operations if ctx.Err() != nil { return MyOutput{Error: "cancelled"} } return MyOutput{...} } ``` ### MCP (Model Context Protocol) Tools ```go import "google.golang.org/adk/tool/mcptool" // Stdio-based MCP server params := mcptool.StdioServerParams{ Command: "npx", Args: []string{"-y", "@modelcontextprotocol/server-filesystem", "/path"}, } tools, closer, err := mcptool.FromServer(ctx, params) defer closer.Close() // HTTP/SSE-based MCP server sseParams := mcptool.SseServerParams{ URL: "http://localhost:8090/sse", Timeout: 5.0, } tools, closer, err := mcptool.FromServer(ctx, sseParams) ``` --- ## Multi-Agent Patterns ### Sequential Agent (Pipeline) Executes agents in order. Each agent's output is available to the next. ```go import "google.golang.org/adk/agent/sequentialagent" // Agent 1: Research researcher, _ := llmagent.New(llmagent.Config{ Name: "researcher", Model: model, Instruction: "Research the given topic thoroughly.", Tools: []tool.Tool{geminitool.GoogleSearch{}}, OutputKey: "research", // Saves to state["research"] }) // Agent 2: Write (uses research output) writer, _ := llmagent.New(llmagent.Config{ Name: "writer", Model: model, Instruction: "Write an article based on: {research}", OutputKey: "draft", }) // Agent 3: Edit editor, _ := llmagent.New(llmagent.Config{ Name: "editor", Model: model, Instruction: "Edit and improve: {draft}", OutputKey: "final", }) // Combine into pipeline pipeline, _ := sequentialagent.New(sequentialagent.Config{ AgentConfig: agent.Config{ Name: "content_pipeline", Description: "Research, write, and edit content", SubAgents: []agent.Agent{researcher, writer, editor}, }, }) ``` ### Parallel Agent (Concurrent Execution) Executes agents simultaneously. Each runs in an isolated branch. ```go import "google.golang.org/adk/agent/parallelagent" // Create specialized agents webSearcher, _ := llmagent.New(llmagent.Config{ Name: "web_searcher", Model: model, Tools: []tool.Tool{geminitool.GoogleSearch{}}, OutputKey: "web_results", }) docAnalyzer, _ := llmagent.New(llmagent.Config{ Name: "doc_analyzer", Model: model, OutputKey: "doc_analysis", }) // Run in parallel parallel, _ := parallelagent.New(parallelagent.Config{ AgentConfig: agent.Config{ Name: "parallel_research", SubAgents: []agent.Agent{webSearcher, docAnalyzer}, }, }) ``` ### Loop Agent (Iterative Refinement) Repeats agents until max iterations or termination condition. ```go import "google.golang.org/adk/agent/loopagent" // Writer produces drafts writer, _ := llmagent.New(llmagent.Config{ Name: "writer", Model: model, Instruction: "Write or improve the draft. Current: {draft}", OutputKey: "draft", }) // Critic evaluates and decides if done critic, _ := llmagent.New(llmagent.Config{ Name: "critic", Model: model, Instruction: `Evaluate the draft: {draft} If excellent, respond with exactly: APPROVED Otherwise, provide specific improvement suggestions.`, OutputKey: "feedback", }) // Loop until approved or max iterations refiner, _ := loopagent.New(loopagent.Config{ AgentConfig: agent.Config{ Name: "refiner", SubAgents: []agent.Agent{writer, critic}, }, MaxIterations: 5, }) ``` ### Hierarchical Delegation Parent agent delegates to specialized sub-agents. ```go // Specialist agents mathAgent, _ := llmagent.New(llmagent.Config{ Name: "math_expert", Description: "Handles mathematical calculations and problems", Model: model, }) codeAgent, _ := llmagent.New(llmagent.Config{ Name: "code_expert", Description: "Writes and explains code", Model: model, Tools: []tool.Tool{geminitool.CodeExecution{}}, }) // Router agent delegates based on query type router, _ := llmagent.New(llmagent.Config{ Name: "router", Model: model, Instruction: `Route queries to the appropriate specialist. Use math_expert for calculations. Use code_expert for programming questions.`, SubAgents: []agent.Agent{mathAgent, codeAgent}, }) ``` --- ## Session & State Management ### State Prefixes | Prefix | Scope | Persistence | | ------- | ----------- | -------------------------- | | (none) | App | Persists across sessions | | `user:` | User | Persists for specific user | | `app:` | Application | Global app state | | `temp:` | Invocation | Cleared after each turn | ### Reading/Writing State ```go // In agent instruction (template syntax) Instruction: "User name: {user:name}, Preferences: {user:preferences}" // In tool function func myTool(ctx tool.Context, input Input) Output { state := ctx.Session().State() // Read name := state.Get("user:name") // Write state.Set("user:preferences", "dark_mode=true") // Delete state.Delete("temp:scratch") } ``` ### Session Services ```go import "google.golang.org/adk/session" // In-memory (development) sessionSvc := session.InMemoryService() // Create session sess, err := sessionSvc.Create(ctx, &session.CreateRequest{ AppName: "my_app", UserID: "user123", State: map[string]any{"user:name": "Alice"}, }) // Get existing session sess, err := sessionSvc.Get(ctx, &session.GetRequest{ AppName: "my_app", UserID: "user123", SessionID: "session-id", }) // List user sessions sessions, err := sessionSvc.List(ctx, &session.ListRequest{ AppName: "my_app", UserID: "user123", }) ``` ### Memory Service (Long-term Knowledge) ```go import "google.golang.org/adk/memory" // In-memory for development memorySvc := memory.InMemoryService() // Store knowledge err := memorySvc.Add(ctx, &memory.AddRequest{ AppName: "my_app", UserID: "user123", Content: "User prefers vegetarian restaurants", }) // Search knowledge results, err := memorySvc.Search(ctx, &memory.SearchRequest{ AppName: "my_app", UserID: "user123", Query: "food preferences", }) ``` --- ## Running Agents Programmatically ### Using the Runner ```go import "google.golang.org/adk/runner" // Create runner r := runner.New(runner.Config{ Agent: myAgent, SessionService: session.InMemoryService(), MemoryService: memory.InMemoryService(), }) // Run agent userMsg := genai.NewContentFromText(genai.RoleUser, "Hello!") for event, err := range r.Run(ctx, "user123", "session123", userMsg, agent.RunConfig{}) { if err != nil { log.Printf("Error: %v", err) continue } // Process events switch { case event.IsFinal(): fmt.Println("Final:", event.Content()) case event.Partial: fmt.Print(event.Text()) // Streaming output case event.ToolCall != nil: fmt.Printf("Calling tool: %s\n", event.ToolCall.Name) } } ``` ### Streaming Modes ```go // Streaming modes agent.RunConfig{ StreamingMode: agent.StreamingModeNone, // Wait for complete response StreamingMode: agent.StreamingModeSSE, // Server-sent events } ``` --- ## Callbacks ### Model Callbacks ```go agent, _ := llmagent.New(llmagent.Config{ BeforeModelCallback: func(ctx agent.CallbackContext, req *model.LLMRequest) (*model.LLMRequest, error) { // Modify request before sending to model log.Printf("Sending to model: %v", req) // Block certain queries if containsForbiddenContent(req) { return nil, errors.New("request blocked") } return req, nil }, AfterModelCallback: func(ctx agent.CallbackContext, resp *model.LLMResponse) (*model.LLMResponse, error) { // Process/modify response log.Printf("Received: %v", resp) return resp, nil }, }) ``` ### Tool Callbacks ```go agent, _ := llmagent.New(llmagent.Config{ BeforeToolCallback: func(ctx agent.CallbackContext, call *tool.Call) (*tool.Call, error) { // Validate tool arguments log.Printf("Tool call: %s(%v)", call.Name, call.Args) // Block dangerous operations if call.Name == "delete_file" { return nil, errors.New("file deletion not allowed") } return call, nil }, AfterToolCallback: func(ctx agent.CallbackContext, call *tool.Call, result *tool.Result) (*tool.Result, error) { // Log or modify results log.Printf("Tool result: %v", result) return result, nil }, }) ``` --- ## Deployment ### Local Development ```bash # Console mode go run main.go # Web UI for testing go run main.go web api webui ``` ### Cloud Run Deployment ```bash # Build the CLI tool go build -o adkgo ./cmd/adkgo # Deploy ./adkgo deploy cloudrun \ -p $GOOGLE_CLOUD_PROJECT \ -r us-central1 \ -s my-agent-service \ -e ./main.go \ --a2a --api ``` ### Manual Docker Deployment ```dockerfile # Dockerfile FROM golang:1.24-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o agent main.go FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /app COPY --from=builder /app/agent . EXPOSE 8080 ENV PORT=8080 CMD ["./agent", "web", "api", "a2a"] ``` ```bash # Build and run docker build -t my-agent . docker run -p 8080:8080 -e GOOGLE_API_KEY=$GOOGLE_API_KEY my-agent ``` ### Environment Variables | Variable | Purpose | | ----------------------- | ------------------------------ | | `GOOGLE_API_KEY` | AI Studio API key | | `GOOGLE_CLOUD_PROJECT` | GCP project ID | | `GOOGLE_CLOUD_LOCATION` | GCP region (e.g., us-central1) | | `PORT` | Server port (default: 8080) | --- ## API Endpoints When running with `web api`: | Endpoint | Method | Purpose | | ----------------------------------------------- | ------ | --------------- | | `/apps/{app}/users/{user}/sessions` | POST | Create session | | `/apps/{app}/users/{user}/sessions` | GET | List sessions | | `/apps/{app}/users/{user}/sessions/{id}` | GET | Get session | | `/apps/{app}/users/{user}/sessions/{id}:run` | POST | Send message | | `/apps/{app}/users/{user}/sessions/{id}:runSse` | POST | Stream response | | `/.well-known/agent-card.json` | GET | A2A agent card | ### Example API Call ```bash # Create session curl -X POST http://localhost:8080/apps/myapp/users/user1/sessions \ -H "Content-Type: application/json" \ -d '{}' # Send message curl -X POST http://localhost:8080/apps/myapp/users/user1/sessions/SESSION_ID:run \ -H "Content-Type: application/json" \ -d '{"message": {"role": "user", "parts": [{"text": "Hello!"}]}}' ``` --- ## Common Patterns ### Error Handling in Tools ```go func myTool(ctx tool.Context, input Input) Output { result, err := externalAPI(input.Query) if err != nil { // Return error message to the model return Output{ Success: false, Error: fmt.Sprintf("API error: %v", err), } } return Output{Success: true, Data: result} } ``` ### Tool with HTTP Client ```go func fetchURL(ctx tool.Context, input URLInput) FetchOutput { client := &http.Client{Timeout: 10 * time.Second} req, _ := http.NewRequestWithContext(ctx, "GET", input.URL, nil) resp, err := client.Do(req) if err != nil { return FetchOutput{Error: err.Error()} } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) return FetchOutput{Content: string(body)} } ``` ### Conditional Tool Availability ```go // Filter tools based on state agent, _ := llmagent.New(llmagent.Config{ ToolFilter: func(ctx agent.InvocationContext, tools []tool.Tool) []tool.Tool { userRole := ctx.Session().State().Get("user:role") if userRole != "admin" { // Remove admin-only tools return filterOutAdminTools(tools) } return tools }, }) ``` ### Graceful Shutdown ```go func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Handle shutdown signals sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigCh log.Println("Shutting down...") cancel() }() // Run agent l := full.NewLauncher() if err := l.Execute(ctx, cfg, os.Args[1:]); err != nil { if ctx.Err() == nil { log.Fatal(err) } } } ``` --- ## Troubleshooting ### Common Issues | Problem | Solution | | --------------------------- | ----------------------------------------------- | | `module not found` | Use `google.golang.org/adk`, not GitHub path | | `API key invalid` | Check `GOOGLE_API_KEY` is set correctly | | `context deadline exceeded` | Increase timeout or check network | | `tool not called` | Improve tool description; make it clearer | | `state not persisting` | Check state prefix; use `user:` for persistence | ### Debug Logging ```go import "log" agent, _ := llmagent.New(llmagent.Config{ BeforeModelCallback: func(ctx agent.CallbackContext, req *model.LLMRequest) (*model.LLMRequest, error) { log.Printf("REQUEST: %+v", req) return req, nil }, AfterModelCallback: func(ctx agent.CallbackContext, resp *model.LLMResponse) (*model.LLMResponse, error) { log.Printf("RESPONSE: %+v", resp) return resp, nil }, }) ``` ### Web UI Debugging Access `http://localhost:8080` when running with `web api webui`: - **Events tab**: See all events in the session - **Request/Response tab**: Raw LLM communication - **Graph tab**: Visualize agent flow - **State tab**: Inspect session state --- ## Resources - **GitHub**: https://github.com/google/adk-go - **Documentation**: https://google.github.io/adk-docs/ - **Go Package**: https://pkg.go.dev/google.golang.org/adk - **Examples**: https://github.com/google/adk-go/tree/main/examples - **Python ADK**: https://github.com/google/adk-python - **Java ADK**: https://github.com/google/adk-java