--- layout: blog title: "Building a Support Agent in Go" permalink: /blog/28 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." --- # Building a Support Agent in Go *June 19, 2026 • Asim Aslam* 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/21) 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/23) 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/26) 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.