e071084ebe
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
94 lines
5.7 KiB
Markdown
94 lines
5.7 KiB
Markdown
---
|
|
layout: blog
|
|
title: "Not Everything Should Be an Agent"
|
|
permalink: /blog/18
|
|
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."
|
|
---
|
|
|
|
# Not Everything Should Be an Agent
|
|
|
|
*June 8, 2026 • By the Go Micro Team*
|
|
|
|
The last two posts were about agents — [the abstraction](/blog/16), and then [agents that plan and delegate](/blog/17), 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/17), 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/14): 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) for the full mapping, or [Plan & Delegate](/docs/guides/plan-delegate) 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).*
|