Every week someone in an n8n thread asks some version of "should this be an AI agent?" Usually the honest answer is "no, but one step inside it might need a model."
I've built ten small-business automations on self-hosted n8n: lead scoring, review replies, invoice extraction, meeting notes, a weekly metrics brief, and so on. Not one of them is an agent in the "give the model tools and let it decide what to do next" sense. Every one is a normal workflow with a single LLM call in the middle, wrapped in plain nodes that check what the model said.
That wasn't a principle when I started. It's where I kept ending up. So before I drop an LLM node (or an AI Agent node) onto the canvas now, I run through five questions. This post is that checklist, with the reasoning behind each question and a real workflow to show where the line falls.
First, what I mean by the three options
People use "agent" for everything, so here's the vocabulary I'll use:
- Plain workflow. Trigger, IF/Switch, Set, Code, HTTP Request. Deterministic. Same input, same output, every time.
- Workflow with an LLM step. Still a fixed path, but one node sends text to a model and gets structured JSON back. The workflow, not the model, decides what happens next.
- Agent. The model chooses which tools to call, in what order, and when it's done. In n8n that's the AI Agent node with tools attached, looping until the model stops.
The jump from 1 to 2 is small. The jump from 2 to 3 is big, because you're moving control flow from your canvas into a prompt. The five questions are mostly about whether that move is worth it.
Question 1: Can I write the rule down?
If I can describe the decision as a rule a new hire could follow without judgment, it doesn't need a model.
- "If the form says budget over $5k and company size over 20, mark it hot." That's an IF node.
- "If the email subject contains 'invoice', route it to accounts." That's a Switch node.
- "Read this free-text form and decide how serious this person sounds." That's not a rule. That needs a model.
The trap is that rules feel boring, and an LLM feels clever. But an IF node never has a bad day, never costs tokens, and you can read exactly why it did what it did.
My test: try writing the rule in one sentence. If you can, build the rule. If you keep writing "...unless it kind of sounds like..." you've found the part that needs a model.
Question 2: Is the input messy human text?
This is the clearest signal for an LLM step. Models are good at turning unstructured text into structured fields: a review into sentiment plus topics, a meeting transcript into decisions and owners, a PDF invoice into vendor, totals and line items.
But notice what that is: parsing, not deciding. The model's job is to turn mess into JSON. Everything after that should be ordinary nodes.
In my lead qualifier, the model reads the raw form submission and returns this:
{
"score": 0,
"tier": "hot | warm | cold",
"summary": "string",
"reasons": ["string"],
"next_step": "string"
}
Then a plain IF node does the deciding: score >= 70 goes to Slack, everything else is just returned in the webhook response. The model never "decides" to message anyone. It produces a number, and a rule I can read acts on that number.
My test: if the input is already structured (a database row, a Stripe event, a form with dropdowns), you probably don't need a model at all. If it's free text written by a human, an LLM step earns its place, as a parser.
Question 3: Is the path fixed, or does it genuinely branch on things I can't predict?
This is the question that separates "LLM step" from "agent."
An agent makes sense when the sequence of actions depends on what it finds along the way, and you can't enumerate the paths ahead of time. For example: "Research this company. If they have a public pricing page, read it; if not, check their docs; if there's a GitHub org, look at recent activity; then summarize." The order and the number of steps really do vary.
Most business automations aren't like that. Lead comes in, score it, alert if hot. Review comes in, classify it, draft a reply, flag it if it's angry. Those paths are fixed. I can draw them. If I can draw the path, I'd rather draw it on the canvas than describe it in a prompt and hope the model follows it.
My test: sketch the flow on paper. If it's a line or a tree with a handful of branches, build it as a workflow. If every run might take a different shape, that's where an agent starts to pay for itself.
Question 4: What's the worst thing that happens if the model is wrong?
This one decides how much freedom the model gets, even when the first three say "use an LLM."
I sort actions into two buckets:
- Reversible or internal: tagging a lead, writing a summary to a sheet, posting to an internal Slack channel. If the model gets it wrong, someone notices and fixes it.
- Outbound or irreversible: emailing a customer, replying to a public review, sending a refund, updating a CRM record other people rely on.
For the second bucket I don't let the model act directly, agent or not. It drafts, and a human or a hard rule approves. My review-reply workflow returns a draft reply plus a needs_owner_attention flag. It doesn't post anything to Google. The prompt also says it must never admit legal liability or offer compensation unless the business's policy allows it, because that's exactly the kind of sentence you can't take back once it's public.
An agent with a "send email" tool attached is the scariest version of this. The model can decide on its own to call that tool. That's the setup behind the horror stories you've probably seen, where an unattended agent emails hundreds of customers overnight. You don't need a smarter model to avoid that. You need the send to sit behind something that isn't the model.
My test: if a wrong output reaches a customer, the model drafts and something else approves. If the only cost is a wrong internal tag, let it act.
Question 5: Can I check the output with code?
If I can validate what the model returned, I can safely use a model. If I can't, I should be nervous.
Every one of my workflows ends the LLM step with the same small Parse node:
const raw = $json.choices?.[0]?.message?.content || '{}';
let out;
try { out = JSON.parse(raw); }
catch (e) { out = { error: 'LLM returned non-JSON', raw }; }
return [{ json: { result: out }, pairedItem: 0 }];
That's the bare minimum: never let a bad response crash the run silently. From there you can check more. Is score actually a number between 0 and 100? Is tier one of the three allowed values? Is the reply under the word limit? Does an extracted invoice total equal the sum of its line items?
Checks like these are cheap with a single LLM step, because there's one output to check. With an agent you'd need to check every intermediate tool call too, and that gets hard fast.
My test: write the validation before the prompt. If you can't think of any way to tell a good output from a bad one in code, either tighten the output format until you can, or keep a human on it.
Scoring it
I don't do anything formal with these. Roughly:
| Answers | What I build |
|---|---|
| Q1 is "yes, I can write the rule" | Plain workflow. No model. |
| Q2 "messy text", Q3 "fixed path" | Workflow with one LLM step that returns JSON, then IF/Switch nodes |
| Q3 "path varies unpredictably" | Consider an agent, but... |
| Q4 "wrong output reaches customers" | ...the model drafts only, whatever you built |
| Q5 "can't validate in code" | Tighten the output format or add a human review step before going further |
For everything I've built so far, the answer landed in row two: a fixed workflow, one structured LLM call, a Parse node, and plain rules after it. That covers lead scoring, review replies, invoice extraction, meeting notes, content repurposing and the rest.
What the "one LLM step" pattern looks like in n8n
If you want to try row two, this is the shape I reuse:
- Trigger: Webhook or Schedule.
-
Config (Code node):
baseUrlandmodel, so you can swap between a paid API and a local OpenAI-compatible server without touching anything else. - Build Prompt (Code node): a strict system prompt that spells out the JSON shape, plus the user content.
-
LLM (HTTP Request): POST to
{{baseUrl}}/chat/completionswithresponse_format: { type: 'json_object' }and a low temperature (I use 0.2). - Parse (Code node): the snippet above, plus whatever validation Q5 gave you.
- Decide (IF/Switch): plain rules on the parsed fields.
- Act: Slack, Sheets, a CRM, or the webhook response.
I use a plain HTTP Request node instead of a provider-specific node so the same workflow runs against OpenAI, Groq, OpenRouter, or a local Ollama /v1 endpoint by changing two values.
When I would reach for an agent
To be fair to agents: they're the right tool when the task is open-ended research or investigation, the tools are read-only (search, fetch a page, query a database), and a human reads the result before anything happens. "Look into this lead and tell me what you find" is a decent agent job. "Look into this lead and email them" is not, at least not without an approval step in between.
Try it
Two of these workflows, the lead qualifier (Q2 in action) and the review reply agent (Q4 in action), are free on GitHub with import steps: https://github.com/arkintof/n8n-ai-agent-workflows
Import one, look at where the LLM step sits, and notice how much of the workflow is still plain nodes.
Now I'm curious about yours: what's the one automation you built as an agent that you later rebuilt as a plain workflow, or the other way round? And which of these five questions would you drop or add? Tell me in the comments.
Written with AI assistance; workflow built and tested by me.
Top comments (0)