# Your n8n Workflow Has 40 Nodes. Should Any of Them Be an AI Agent?

> Source: <https://dev.to/hosseinhezami/your-n8n-workflow-has-40-nodes-should-any-of-them-be-an-ai-agent-14bk>
> Published: 2026-09-09 17:38:06+00:00

Open an n8n workflow with 40 nodes and two thoughts usually appear at the same time.

First: *This is getting out of hand.*

Second: *Could an AI agent replace half of it?*

Sometimes the answer is yes. Often, the answer is: *Only a small part, and only if you keep the dangerous bits deterministic.*

A large workflow is not automatically a bad workflow. Forty nodes can represent forty clear, testable, observable steps. Or they can represent a fragile pile of string parsing, nested IF nodes, retry hacks, and manual exception handling that is begging for a smarter component.

The trick is knowing which kind of complexity you have.

**TL;DR**

n8n workflows tend to grow in a very natural way.

You start with a webhook. Then you add a filter. Then an API call. Then a Switch node. Then an error path. Then a retry. Then a formatting step. Then another API. Then a Slack message. Then a database lookup. Then another branch for a special customer.

Before long, the canvas looks like a subway map.

At that point, an AI agent can look attractive because it promises to collapse complexity into intent:

“Read the incoming message, figure out what needs to happen, use the right tools, and produce the result.”

That is powerful. It is also exactly the kind of power that can make a production system harder to reason about.

The right question is not:

“Can an agent replace this workflow?”

It is:

“Which nodes are doing deterministic work, which nodes are compensating for ambiguity, and which nodes are performing actions that should never be uncontrolled?”

That distinction is the whole game.

**Scenario:**

Your n8n workflow has 40 nodes. Some are HTTP Request nodes, some are Switch nodes, some handle retries, some format payloads, and some write to a database. It works. It is just visually intimidating.

**Why it matters:**

A node count is a weak signal of quality.

A workflow with 40 small, explicit steps can be easier to operate than a workflow with 3 magical black boxes. At least with explicit nodes you can see:

An agent can reduce visual complexity while increasing behavioral complexity. That trade is not always worth it.

**Solution:**

Before adding an agent, classify your nodes.

A useful audit looks like this:

| Node category | Example | Keep deterministic? | Agent candidate? | 
|---|---|---|---|
| Input validation | Required fields, auth checks | Yes | No | 
| Business rules | Refund eligibility, SLA calculation | Yes | Rarely | 
| External API calls | CRM lookup, ticket creation | Yes | Maybe as tool | 
| Formatting | JSON to CSV, date normalization | Yes | No | 
| Routing | Known categories | Yes | Maybe if fuzzy | 
| Text interpretation | Emails, support messages, notes | Sometimes | Often | 
| Side effects | Send email, update record, charge card | Yes | No, not directly | 
| Error handling | Retry, fallback, alerting | Yes | No | 

This audit usually reveals something important: the workflow is not “40 nodes of confusion.” It is mostly deterministic plumbing with a few ambiguous interpretation steps hidden inside.

Those ambiguous steps are where an agent may belong.

💡 Practical note:

If a workflow is hard to understand because nobody knows what the business rules are, replacing it with an agent will not fix the problem. It will hide the problem inside a prompt.

**Scenario:**

Your workflow receives support emails. You have a chain of IF nodes checking whether the subject contains “refund”, “invoice”, “login”, or “broken”. It works until someone writes, “I was charged twice and cannot access my account.”

Now the ticket matches two categories, or none.

**Why it matters:**

This is where deterministic automation often becomes fragile: unstructured human language.

A deterministic workflow is excellent when inputs are structured:

It becomes much less pleasant when inputs are messy:

That is a natural place for an LLM or agent-assisted step.

**Solution:**

Use the model to extract structured data, then use normal n8n nodes to apply business logic.

For example, instead of asking the agent to decide the final action, ask it to produce a constrained object:

```
{
  "intent": "billing_issue",
  "sub_intent": "duplicate_charge",
  "account_email": "user@example.com",
  "order_id": "ORD-12345",
  "urgency": "high",
  "summary": "Customer says they were charged twice and cannot log in.",
  "confidence": "medium"
}
```

Then your workflow can validate and route that object deterministically.

``` js
// n8n Code node example
const allowedIntents = [
  "billing_issue",
  "account_access",
  "bug_report",
  "feature_request",
  "unknown",
];

const extracted = $json.extracted;

if (!extracted || typeof extracted !== "object") {
  throw new Error("Extraction result is missing.");
}

if (!allowedIntents.includes(extracted.intent)) {
  return [{
    json: {
      route: "human_review",
      reason: `Unrecognized intent: ${extracted.intent}`,
      extracted,
    },
  }];
}

if (!extracted.account_email && !extracted.order_id) {
  return [{
    json: {
      route: "ask_for_details",
      reason: "Missing account identifiers.",
      extracted,
    },
  }];
}

return [{
  json: {
    route: extracted.intent,
    extracted,
  },
}];
```

The agent handles ambiguity. The workflow handles consequences.

**Why this works:**

You are not asking the model to run your business. You are asking it to convert messy input into a shape your automation can understand.

That is one of the safest and most useful places to put an AI component.

**Scenario:**

Your workflow routes incoming requests to billing, support, sales, or engineering. You have 12 IF nodes, some regex, and a few “else” branches nobody fully trusts.

The obvious fix seems to be:

“Let an agent classify the message.”

Sometimes that is correct. Sometimes you are just replacing a cheap, predictable router with an expensive, nondeterministic one.

**Why it matters:**

Routing is a classic automation decision. If the categories are stable and the signals are clear, deterministic routing is better.

A Switch node or simple Code node is:

An AI classifier is useful when categories are fuzzy, language is varied, and the routing logic would otherwise become an unmaintainable pile of string matching.

**Solution:**

Start deterministic. Move to model-assisted routing only when deterministic routing fails in practice.

A simple deterministic router in an n8n Code node might look like this:

``` js
const subject = ($json.subject ?? "").toLowerCase();
const body = ($json.body ?? "").toLowerCase();
const text = `${subject} ${body}`;

let route = "general";

if (text.includes("refund") || text.includes("invoice") || text.includes("charge")) {
  route = "billing";
} else if (text.includes("password") || text.includes("login") || text.includes("2fa")) {
  route = "account_access";
} else if (text.includes("error") || text.includes("bug") || text.includes("crash")) {
  route = "engineering";
}

return [{ json: { route } }];
```

This is not glamorous, but it is operationally boring. Boring is valuable.

If the categories become ambiguous, you can use a model to classify into a fixed enum:

```
{
  "route": "billing",
  "confidence": 0.92,
  "reason": "Customer mentions a duplicate charge."
}
```

Then keep a deterministic fallback:

``` js
const result = $json.classification;

if (!result || !["billing", "account_access", "engineering", "general"].includes(result.route)) {
  return [{ json: { route: "human_review" } }];
}

if (typeof result.confidence === "number" && result.confidence < 0.7) {
  return [{ json: { route: "human_review", reason: "Low classification confidence." } }];
}

return [{ json: { route: result.route } }];
```

**Why this works:**

You preserve deterministic behavior where possible and only introduce nondeterminism where the problem is genuinely fuzzy.

⚠️ Gotcha:

If the agent can route a message but cannot explain why, you have made debugging harder. Ask for a route, confidence, and short rationale.

**Scenario:**

A user asks:

“Find the latest invoice for Acme Corp, check whether it was paid, and if not, draft a polite reminder to the billing contact.”

This looks simple, but the workflow path depends on what it discovers.

If the customer has one invoice, the path is simple. If there are ten invoices, the agent may need to filter. If the invoice status is missing, it may need another lookup. If the billing contact is missing, it may need to search the CRM.

This is different from a fixed pipeline.

**Why it matters:**

Deterministic workflows shine when the path is known:

Agents shine when the path is dynamic:

That loop is the core value of an agent.

**Solution:**

Use an agent for bounded investigation tasks, not for the entire business process.

Good agent tasks inside n8n include:

Less appropriate agent tasks include:

**Why this works:**

You use the agent where adaptability matters. You keep the workflow where predictability matters.

A useful rule:

If the workflow can be drawn as a stable flowchart, keep it as a workflow.

If the workflow keeps growing branches because the world is messy, consider an agent for the messy part.

**Scenario:**

You give an agent access to tools like `updateTicket`, `sendEmail`, `createRefund`, and `deleteRecord`. It mostly works. Then one ambiguous request causes it to email the wrong person or close a ticket that should stay open.

**Why it matters:**

An agent that can decide and act is more powerful, but also more dangerous.

The problem is not that models are useless. The problem is that production systems need boundaries. If the agent can directly perform side effects, every prompt ambiguity becomes a potential operational incident.

**Solution:**

Separate decision-making from execution.

The agent can propose an action. The workflow should decide whether that action is allowed.

A simple tool-policy wrapper might look like this:

``` js
const MUTATING_TOOLS = new Set([
  "updateTicket",
  "sendEmail",
  "createRefund",
  "closeAccount",
]);

function authorizeToolCall(toolCall, context) {
  const { name, args } = toolCall;

  if (!name) {
    return { allowed: false, reason: "Tool name missing." };
  }

  if (MUTATING_TOOLS.has(name) && !context.allowMutations) {
    return {
      allowed: false,
      reason: "Mutating tools are disabled for this workflow run.",
    };
  }

  if (name === "sendEmail") {
    const to = String(args.to ?? "").toLowerCase();

    if (!to.endsWith("@yourcompany.example")) {
      return {
        allowed: false,
        reason: "External email sends require explicit approval.",
      };
    }
  }

  if (name === "createRefund" && Number(args.amount ?? 0) > 500) {
    return {
      allowed: false,
      reason: "Refunds above threshold require human approval.",
    };
  }

  return { allowed: true };
}
```

In an n8n workflow, this kind of logic can sit between the agent output and the actual action nodes.

The flow becomes:

```
Agent proposes tool call
→ policy check
→ validation
→ approval gate if needed
→ deterministic execution node
→ audit log
```

Not:

```
Agent thinks
→ Agent acts
→ Hope
```

**Why this works:**

It gives you the adaptability of an agent without giving it unchecked authority over your systems.

🚨 Production warning:

If an agent can write to your database, send external messages, or trigger payments, it needs guardrails, logging, and probably a human approval path.

**Scenario:**

The agent returns a helpful paragraph:

“It looks like the customer is asking for a refund because their order arrived late. I recommend processing it, but the order number might be ORD-12345.”

Your workflow now has to parse that prose. If the wording changes, the automation breaks.

**Why it matters:**

A workflow needs stable data, not vibes.

If an agent returns free-form text, you have moved the ambiguity problem from the beginning of the workflow to the middle of it.

**Solution:**

Require structured output.

The agent should return something like:

```
{
  "intent": "refund_request",
  "order_id": "ORD-12345",
  "reason": "late_delivery",
  "recommended_action": "review_refund",
  "confidence": "medium",
  "missing_fields": []
}
```

Then validate it before using it.

``` js
const raw = $json.agent_output;

let parsed;

try {
  parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
} catch {
  throw new Error("Agent output was not valid JSON.");
}

const allowedIntents = [
  "refund_request",
  "order_status",
  "account_access",
  "technical_issue",
  "unknown",
];

const allowedActions = [
  "no_action",
  "ask_for_details",
  "route_to_support",
  "review_refund",
  "human_review",
];

if (!allowedIntents.includes(parsed.intent)) {
  throw new Error(`Invalid intent: ${parsed.intent}`);
}

if (!allowedActions.includes(parsed.recommended_action)) {
  throw new Error(`Invalid recommended_action: ${parsed.recommended_action}`);
}

if (parsed.intent === "refund_request" && !parsed.order_id) {
  parsed.recommended_action = "ask_for_details";
  parsed.missing_fields = ["order_id"];
}

return [{ json: parsed }];
```

This does not require the model to be perfect. It requires the workflow to accept only usable output.

**Why this works:**

Structured output turns the agent into a component with an interface. That interface can be validated, tested, logged, and rejected when necessary.

🔍 Why this matters:

If you cannot validate an agent’s output, you cannot safely automate based on it.

**Scenario:**

Your workflow runs every minute. Each run calls an agent. The agent sometimes retries, sometimes expands its reasoning, sometimes calls multiple tools. Latency becomes unpredictable. Costs creep upward.

**Why it matters:**

An agent is not just another function node. It may involve:

That makes it behave more like a slow, expensive, nondeterministic external service.

Production workflows need budgets for that kind of service.

**Solution:**

Give the agent explicit limits and fallback behavior.

A practical budget object might look like this:

``` js
const agentBudget = {
  maxSeconds: 20,
  maxToolCalls: 4,
  maxRetries: 1,
  maxInputTokens: 4000,
  fallbackRoute: "human_review",
};
```

Then enforce those limits around the agent call.

In n8n terms, that may mean:

A fallback is not optional.

If the agent times out, returns invalid JSON, exceeds budget, or produces low confidence output, the workflow should do something deliberate:

**Why this works:**

It prevents an agent from becoming an unbounded cost and latency multiplier inside an otherwise normal automation pipeline.

A good mental model:

An agent should be treated like a contractor with a limited scope, a deadline, and a requirement to return a specific form.

Not like an employee with unlimited access and no review process.

The best production answer is rarely:

“Replace the whole workflow with an agent.”

It is usually:

“Keep the deterministic spine. Add agents at the joints where the world is messy.”

A deterministic spine is the part of the workflow that must be reliable:

Agentic joints are the places where ambiguity enters:

A production-friendly shape often looks like this:

```
Webhook / trigger
→ validate payload
→ normalize input
→ agent: extract/classify/summarize
→ validate agent output
→ deterministic routing
→ policy check
→ action node
→ audit log
→ error fallback
```

This gives you several advantages:

It also makes the workflow easier to explain to non-developers.

You can say:

“The AI step reads the message and extracts structured fields. The workflow decides what happens next.”

That is much easier to trust than:

“The AI decides.”

If I were looking at a 40-node n8n workflow and deciding whether to introduce an AI agent, I would use a simple framework.

Examples:

A useful comparison:

| Workflow problem | Better approach | 
|---|---|
| Fixed API sequence | Normal n8n workflow | 
| Known routing rules | Switch / IF / Code node | 
| Messy email interpretation | AI extraction step | 
| Dynamic tool selection | Bounded agent | 
| Refund calculation | Deterministic logic | 
| Drafting a response | Agent with human review | 
| Sending money or deleting data | Deterministic approval flow, not direct agent action | 
| Audit trail | Explicit workflow nodes and logs | 

The core question is not whether AI agents are impressive. They can be.

The core question is whether a particular node in your workflow needs judgment, or whether it needs reliability.

If the node is doing math, enforcement, permissions, or side effects, keep it deterministic.

If the node is staring at messy human input and trying to figure out what it means, that is where an agent may finally earn its place.
