---
title: "Not Everything Should Be an Agent"
linkTitle: "Not Everything Should Be an Agent"
date: 2026-06-08
description: "We spent two posts on agents that plan and delegate. Here's the other half: when the path is known, you want a workflow — predictable, event-driven, deterministic. In Go Micro they're the same building blocks, two modes."
category:
- opinion
tags:
- AI
- agents
- workflows
aliases:
  - /blog/18
---
The last two posts were about agents — [the abstraction](/blog/2026/06/05/introducing-micro-newagent.html), and then [agents that plan and delegate](/blog/2026/06/07/agents-that-plan-and-delegate.html), directing their own work over many turns. That's the exciting part. It's also, honestly, the part you should reach for *least often*.

An agent decides its own path at runtime. That's powerful when the task genuinely needs it, and a liability when it doesn't — you trade predictability, latency, and cost for flexibility you may not want. Most real work has a known shape: *when this event happens, do these things.* For that, you don't want a model improvising. You want a **workflow**.

The distinction is the one Anthropic draws in [Building Effective Agents](https://www.anthropic.com/engineering/building-effective-agents): a **workflow** is LLMs and tools orchestrated through *predefined* paths; an **agent** is an LLM *dynamically directing* its own process. Determinism is the dividing line. Go Micro has both — and they're the same building blocks underneath.

## A workflow is a Flow

In Go Micro, the predefined-path side is a `Flow`. It subscribes to an event and runs one defined step: a prompt, with your services available as tools.

```go
f := micro.NewFlow("onboard-user",
    micro.FlowTrigger("events.user.created"),
    micro.FlowPrompt("New user {{.Data}} — create a workspace and send a welcome email."),
    micro.FlowProvider("anthropic"),
)
```

When a `user.created` event lands on the broker, the flow fires. There's no open-ended loop, no self-direction — a known trigger runs a known step. You can read exactly what it will do. That's the point.

## Same building blocks, two modes

Here's what makes this coherent rather than two competing systems: a workflow and an agent are built from the *same* primitive — the augmented LLM. A model, with every service endpoint already available as a tool, and the store as memory. Go Micro gives you that for free; every endpoint is a tool the moment a service registers.

The only difference is **who decides the path**:

- **You decide it** → a workflow (`Flow`). The trigger and the step are fixed.
- **The model decides it** → an agent (`Agent`). It plans, calls tools, evaluates, and chooses the next step.

It's not two frameworks. It's one set of pieces, pointed two ways.

## Flow triggers, Agent reasons

Sometimes a workflow's step genuinely needs judgment — the path isn't fully knowable in advance. You don't have to choose globally. A flow can *hand off* to an agent: the workflow stays the deterministic trigger, and the agent does the open-ended part.

```go
f := micro.NewFlow("onboard-user",
    micro.FlowTrigger("events.user.created"),
    micro.FlowPrompt("New user {{.Data}} — get them set up."),
    micro.FlowAgent("conductor"),   // the flow triggers; the conductor agent reasons
)
```

Now the event fires the flow, the flow renders the prompt, and a registered `conductor` agent handles it over RPC — with its full toolkit: [plan, delegate](/blog/2026/06/07/agents-that-plan-and-delegate.html), memory, and guardrails. **Flow triggers, Agent reasons.** The deterministic and dynamic halves compose along one clean seam, because an agent is just a service and the hand-off is just an RPC.

## Predictability, even in agents

Choosing an agent doesn't mean giving up control. Anthropic is emphatic that autonomous agents need stopping conditions and human checkpoints, and Go Micro's agent has both — as plain options, not a framework:

```go
micro.NewAgent("conductor",
    micro.AgentServices("task"),
    micro.AgentMaxSteps(8),                 // a stopping condition
    micro.AgentApproveTool(approveBilling),  // a human-in-the-loop gate
)
```

`MaxSteps` bounds how many actions the agent may take. `ApproveTool` gates each action before it runs — return `false` and it's blocked, with the reason fed back to the model. These are guardrails: a counter and a callback on the path every tool call already takes. No new abstraction.

## Which one do you reach for?

The honest order, smallest first:

1. **A single model call.** Most tasks need nothing more — one augmented LLM, one service call. Start here.
2. **A workflow (`Flow`).** When the path is well-defined and you want it to be predictable and event-driven.
3. **An agent (`Agent`).** When the task genuinely needs flexibility and model-driven decisions — and you accept the cost, and add the guardrails.

The mistake is starting at 3. Agents are the most capable tool and the easiest to over-apply. Reach for the simplest thing that does the job, and move up only when the job demands it.

## One set of pieces

We didn't build a workflow engine and an agent framework. We built services that are tools, and then pointed an LLM at them two ways — a predefined path, or a dynamic one. `Flow` and `Agent` are modes, not frameworks, and they compose because they share everything underneath. That's the same principle we've held since [going all in on AI](/blog/2026/06/04/going-all-in-on-ai.html): services are the only abstraction, the LLM calls them as tools, and everything else is how you arrange them.

Read the [Agents and Workflows guide](/docs/guides/agents-and-workflows.html) for the full mapping, or [Plan & Delegate](/docs/guides/plan-delegate.html) for the agent side.

```bash
curl -fsSL https://go-micro.dev/install.sh | sh
```

---

*Go Micro is open source. Star us on [GitHub](https://github.com/micro/go-micro), join the [Discord](https://discord.gg/G8Gk5j3uXr), or read the [docs](https://go-micro.dev/docs).*
