---
title: "Building a Support Agent in Go"
linkTitle: "Building a Support Agent in Go"
date: 2026-06-19
description: "A real thing you can build with Go Micro: a support desk where a customer ticket triggers an agent that looks up the customer, sets priority, and replies — with a human-in-the-loop gate on the one action that touches a customer."
category:
- tutorial
tags:
- AI
- agents
- tutorial
aliases:
  - /blog/28
---
Most agent demos are a chat box wired to one tool. Real systems aren't that — they're a handful of services, an agent that operates them, something that triggers the agent without a human typing, and a gate on the actions you don't want it taking on its own. Here's that, built end to end. The full code is in [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support); it runs with no API key.

The scenario: a customer files a ticket. That should trigger an agent to look the customer up, set a priority, and reply — but it must not email anyone without passing an approval gate.

## 1. Services are the tools

Start with plain services. The agent will discover their endpoints as tools automatically — you don't describe them twice.

```go
type CustomerService struct{}

// Lookup returns the customer with the given email.
// @example {"email": "alice@acme.com"}
func (s *CustomerService) Lookup(ctx context.Context, req *LookupRequest, rsp *Customer) error {
    // ...
}
```

A `tickets` service (with `Update`) and a `notify` service (with `Send`, the action we'll gate) round it out. Three ordinary Go Micro services — nothing AI-specific about them.

## 2. The agent

An agent is a service with a model inside. Give it the services it manages and it turns their endpoints into tools:

```go
support := micro.NewAgent("support",
    micro.AgentServices("customers", "tickets", "notify"),
    micro.AgentPrompt("You are a support agent. For each ticket, look up the "+
        "customer, set a priority, and reply. Escalate billing issues."),
)
```

That's the whole agent. It discovers `customers.Lookup`, `tickets.Update`, and `notify.Send`, and the model decides which to call.

## 3. The event is the prompt

A support agent that waits for someone to type into a chat box is useless. The trigger is a *ticket*, so a flow turns that event into the agent's work:

```go
intake := micro.NewFlow("intake",
    micro.FlowTrigger("events.ticket.created"),
    micro.FlowAgent("support"),
    micro.FlowPrompt("A new support ticket arrived: {{.Data}}. Handle it."),
)
```

Now a `ticket.created` event on the broker is enough to set the agent going — no human in the loop to start it. ([When the event is the prompt](/blog/2026/06/15/when-the-event-is-the-prompt.html) is the idea in full.)

## 4. The guardrail is the point

The agent can read and triage all it likes. The action you actually care about is the one that reaches a customer — sending the email. That goes through a gate:

```go
micro.AgentApproveTool(func(tool string, input map[string]any) (bool, string) {
    if strings.Contains(tool, "Send") {
        // return false to hold it for a human or a policy
        log.Printf("approval gate: emailing %v", input["to"])
    }
    return true, ""
})
```

Return `false` and the send is refused with a reason the model sees — that's your human-in-the-loop, your spend cap, your billing sign-off. The agent never gets to email a customer on its own unless you let it. ([Agent guardrails](/blog/2026/06/16/agent-guardrails.html) covers the rest.)

## Run it

```
> event: events.ticket.created {"id":"ticket-1","customer":"alice@acme.com",...}

    [customers] looked up Alice (pro plan)
    [tickets]   ticket-1 → priority=high status=in_progress
    ▣ approval gate notify_NotifyService_Send(alice@acme.com) — approved
    [notify]    📨 to=alice@acme.com: "Hi Alice — thanks for reaching out..."

✓ ticket triaged and the customer was replied to — triggered by an event
```

With the mock model it follows a fixed triage so it runs anywhere. Point it at a real model — `go run main.go -provider anthropic` — and the agent reasons about the ticket itself, choosing what to look up and how to reply.

## What you actually built

Count what's AI-specific: the prompt, and one model option. Everything else is services, a flow, the broker, and a guardrail — the things Go Micro has always done. The agent didn't need a framework of its own; it's a service that calls other services, triggered by an event, with a gate on the dangerous action.

That's the shape of a real agent system, and it's the same shape as a real service system. From here you'd make the gate enforce a real policy, add a knowledge-base service for the agent to search, or expose the agent over [A2A](/blog/2026/06/18/agents-across-frameworks-a2a.html) so another team's agent can file tickets. The code is in [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support) — clone it and change one thing.
