Module 5: Multi-Agent Systems: Agents That Delegate Tasks

4. An agent as another agent's tool: native delegation

Description

By the end of this lesson you'll be able to connect a complete agent — with its own model, its own system prompt, and its own tools — to another agent's ai_tool port, so the first one can delegate work to it whenever the model decides; you'll be able to read the nested trace that delegation produces to know exactly what happened at each level; and you'll be able to choose, with judgment, between the two native mechanisms n8n offers for this, knowing what each one wins and what each one gives up.

This matters because it's the point where the previous two lessons' design stops being a drawing. Up to now you have a paper split of responsibilities and a table of who owns what. This lesson is the wire. And it's also, without exaggeration, the reason this module exists: for years, in n8n, "agents delegating tasks to each other" was a figure of speech for describing a Switch with AI nodes on each branch. Today it's literal — the specialist connects to the same port where you connected a Gmail node in Module 4, and the calling agent doesn't know or care that there's another agent reasoning on the other end. To it, it's just another tool. That symmetry is the entire trick, and understanding it well is what separates someone who builds multi-agent systems from someone who describes them.

Connection to the module: lesson 3 gave you the architecture — orchestrator with the conversation and the memory, workers with their domains and their tools — and left you with the wiring pending. This lesson resolves it. It also brings back two things from Module 4 almost unchanged: a tool's contract from lesson 5 (name and description are the only thing the calling agent has to decide with) and $fromAI() from lessons 3 and 6 (the mechanism by which the model builds the call's arguments). What's still incomplete here — exactly what shape the specialist's returned response takes, and what happens when it can't resolve something — is lesson 5.

From a tool that does one thing to a tool that thinks

Think of the difference between a calculator and an accountant.

When you give a calculator a number, you get exactly what you asked for. There's no judgment involved: you put in 1200 * 0.19, out comes 228. If you give it bad data, it produces a bad result with the same confidence. It's useful precisely because it's predictable.

When you send something to an accountant, something else happens. You tell them "check this $1,200 charge the customer doesn't recognize" and they decide what to look at first. They check the record, find nothing, it occurs to them the purchase might have been made under another name, they check that too, and only then do they respond. You didn't give them a sequence — you gave them an assignment. The steps were theirs to choose, based on what they found along the way.

Module 4's tools are calculators. A Gmail node connected to the ai_tool port does exactly one thing: sends an email with the parameters it receives. A Postgres node queries and returns rows. Predictable, and that's their virtue.

What you're going to connect in this lesson is an accountant. From the calling agent's point of view, it looks identical: a tool with a name, a description, and a parameter. It connects to the same port. The agent decides to call it the same way, comparing its description against what it needs. But on the other end of the wire, instead of a node that executes an action, there's a complete agent that's going to reason, choose between its own tools, evaluate results, and maybe call three tools before responding.

That symmetry isn't an implementation detail: it's what makes the pattern scale. If delegating to an agent required a different mechanism than using a tool, you'd have to design the orchestrator differently. Because it's the same mechanism, everything you learned about tool contracts applies without translation, and an orchestrator can have a mix of agents and regular tools with no conceptual problem.

The AI Agent Tool node

n8n 2.0 has a specific sub-node for this: AI Agent Tool. Its internal type is @n8n/n8n-nodes-langchain.agentTool, and the key word in that string is the last one: agentTool, not agent. It's an agent packaged as a tool.

The difference from the AI Agent node you already know is one of position in the graph, not of nature:

AI Agent (root)AI Agent Tool (sub-node)
Category in n8nRoot nodeSub-node of type tool
How its work comes inThrough the main connection, from a triggerThrough another agent's ai_tool port, when that agent decides to call it
Who decides it runsThe flow (a message arrived)The model of the agent it's connected to
Where its output goesThrough the main connection, to the next nodeBack to the agent that called it, as the tool's result
Does it have its own sub-nodes?YesYes — it also has its own Chat Model, Memory, and Tool ports

That last row is the important one, and it's what makes everything else possible. An AI Agent Tool isn't a "reduced" agent: it has its own ports underneath, and its own model and its own tools hang off it, same as off the root agent. On the canvas it looks like a second level of branching: three agent-tools hang off the root agent, and each one's own tools hang off it.

The ports

PortConnection typeRequired?Notes
Chat Modelai_languageModelYesCan be a different model than the calling agent's
Toolai_toolNo, but with no tools it can only talk to itselfIts domain's tools go here
Memoryai_memoryNoBy lesson 3's rule, it's normally left empty
Output upwardai_toolYesConnects to the Tool port of the agent that's going to call it

The parameters

These are the fields you configure in the node's panel. Let's go one by one, because each one answers a different question.

Description. The text the calling agent reads to decide whether this tool applies. It's exactly the same field, with exactly the same function, as the Description of any Module 4 tool — with one difference of emphasis: here you don't describe an action, you describe a scope of competence. "Sends an email" is an action's description; "resolves any case related to charges, unrecognized charges, and billing" is a scope's description. And as in Module 4, the part that pays off the most is saying when NOT to use it.

Source for Prompt (User Message). Where this agent's assignment comes from. When the node is used as a tool, the option that serves you is defining it yourself on the node (in the panel it shows up as defining it below, in the node itself), because the assignment doesn't come from a chat trigger — it comes from the calling agent.

The prompt text (User Message). The assignment goes here. And this is where $fromAI() comes in: you don't write a fixed assignment, you write an expression that tells the calling agent's model what to draft. It's the same mechanism from Module 4, applied to something more interesting than an order_id:

{{ $fromAI("task", "Complete, self-contained description of the case this specialist must resolve. Include every piece of data you already gave or the customer mentioned (IDs, amounts, dates). The specialist has no access to the conversation.", "string") }}

Read that description carefully. It's doing two jobs: it tells the model what to write, and it reminds it of an architecture constraint — the specialist doesn't see the conversation — that if it isn't written there, the model has no way of knowing. A $fromAI("task", "The task") without that clarification produces assignments like "check what they asked me," which the specialist can't resolve because it doesn't have the "asked me."

Options → System Message. The specialist's system prompt. Short, single-domain, with its rules and its output format — the one you wrote in exercise 3 of lesson 3.

Options → Max Iterations. How many reason-act-observe turns this agent can take before giving up. It comes with a default value — 10 in the AI Agent node — and here it matters twice as much as in a standalone agent, because you're nesting loops. Lesson 6 is dedicated entirely to this.

Options → Return Intermediate Steps. Whether the specialist's output includes the detail of its own tool calls. Turn it on while building: without this, when a specialist returns something odd you're only going to see its conclusion, with no way to know which tools it checked to get there. In production you can turn it off if the volume of logs bothers you.

Honest note about labels. n8n moves and renames fields between minor versions fairly often — the AI Agent node itself stopped having an "agent type" selector in 1.82, and the configuration panel got redesigned in 2.0. The concepts above are stable: description for the caller, source of the assignment, own system prompt, iteration limit, trace. Verify the exact names and which tab each one lives in on your instance's node panel before assuming they're where a tutorial says. If something doesn't show up under that name, look for the concept, not the exact string.

Worked example: connecting billing_specialist to triage_agent

Let's wire up the system you designed in lesson 3. Let's start with a single specialist to see the mechanism clearly, and the rest is repetition after that.

Step 1 — The orchestrator already exists. On the canvas you have a Chat Trigger connected by main to an AI Agent node called triage_agent, with its Chat Model and its Postgres memory connected to the ai_memory port. Its ai_tool port is empty for now.

Step 2 — You add the AI Agent Tool node. You search for "AI Agent Tool" in the node panel and drop it on the canvas. You rename it to billing_specialist. The node's name matters: it's what you're going to see in the trace and it's part of how the calling agent refers to this tool.

Step 3 — You connect its output to the orchestrator. You drag from billing_specialist's top connector to triage_agent's Tool port. The connection created is of type ai_tool — the same curved line you already used to connect a Gmail node. Visually there's no difference at all, and that's exactly what we want.

Step 4 — You connect its own model. You connect a chat model node to billing_specialist's Chat Model port. Notice it doesn't have to be the same one the orchestrator uses: here you can put the most capable model you have available, because this agent makes decisions about money, while the orchestrator only decides who to call.

Step 5 — You connect its tools. You connect its three domain tools to billing_specialist's Tool port: lookup_charge, get_customer_profile, and open_dispute. Each with its Description and its $fromAI() well written, as you learned in Module 4.

Step 6 — You configure the node. This is the real content:

# Node: AI Agent Tool — Name: billing_specialist
# (connected to triage_agent's Tool port)

Description:
  Resolves charge cases, unrecognized charges, invoice amounts,
  and dispute requests. Use it when the customer mentions a
  charged amount, a card, an account statement, or an invoice.
  Do NOT use it for order status, shipments, or product
  returns: order_specialist exists for that.
  Returns a summary of the finding and the case's status; it doesn't
  return text to show the customer as-is.

Source for Prompt (User Message):
  Defined in this node

Prompt (User Message):
  {{ $fromAI(
       "task",
       "Complete, self-contained assignment for the billing
        specialist. It must include the amount, the charge's
        approximate date, the customer's ID, and what the resolution
        should look like. This specialist does NOT have access to the
        conversation history: any relevant data the customer
        mentioned in earlier turns must be written here.",
       "string"
     ) }}

Options:
  System Message:
    You are TuTienda's billing specialist. You resolve charges
    the customer doesn't recognize, amount questions, and dispute
    requests. You don't handle product returns or shipping status:
    if the assignment belongs to that domain, don't use any tool
    and report it as out of your scope.

    Procedure: use lookup_charge with the amount and the approximate
    date. If it doesn't show up, check get_customer_profile in case
    the purchase was made under another name or with another card
    from the same customer. Only if it still doesn't show up, use
    open_dispute.

    Never promise a refund or a resolution deadline. You can
    confirm the dispute got opened and its reference number.

    You work with the assignment you receive; you don't have a history
    of the conversation. If you're missing a piece of data, don't make it up:
    report it.

    Return: what you found, what action you took, whether the case is
    closed or pending, and what data is missing if it's pending.
    Don't write greetings or sign-offs: your output is read by another agent.

  Max Iterations: 6
  Return Intermediate Steps: true

Step 7 — You tell the orchestrator it exists. The triage_agent can already see the tool, because it's connected. But its system prompt has to declare the usage policy:

# Node: AI Agent — Name: triage_agent
# Options → System Message:

  You are TuTienda's first line of support. Your only job is to
  understand what the customer needs and delegate it to the right
  specialist. You don't check systems or resolve cases on your own.

  Available specialists:
  - billing_specialist: charges, fees, billing, disputes.
  - order_specialist: orders, shipments, delays, returns.
  - sales_specialist: product recommendations and pricing.

  Rules:
  - If the message carries more than one topic, delegate each topic
    separately and compose a single response at the end.
  - Every assignment you send must be self-contained: include the
    data the customer gave at any turn of the conversation, because
    specialists don't see the history.
  - If a specialist reports the case is pending due to a missing
    piece of data, ask the customer for it before delegating again.
  - Don't make up domain information. If no specialist applies,
    say so honestly.
  - Compose the final response to the customer with a warm tone, in a
    single voice, without repeating greetings.

Step 8 — How it looks exported. If you export the workflow as JSON, the part that matters is connections, because that's where you see the real graph:

{
  "connections": {
    "billing_specialist": {
      "ai_tool": [[ { "node": "triage_agent", "type": "ai_tool", "index": 0 } ]]
    },
    "Anthropic Chat Model (billing)": {
      "ai_languageModel": [[ { "node": "billing_specialist", "type": "ai_languageModel", "index": 0 } ]]
    },
    "lookup_charge": {
      "ai_tool": [[ { "node": "billing_specialist", "type": "ai_tool", "index": 0 } ]]
    },
    "get_customer_profile": {
      "ai_tool": [[ { "node": "billing_specialist", "type": "ai_tool", "index": 0 } ]]
    },
    "open_dispute": {
      "ai_tool": [[ { "node": "billing_specialist", "type": "ai_tool", "index": 0 } ]]
    },
    "Postgres Chat Memory": {
      "ai_memory": [[ { "node": "triage_agent", "type": "ai_memory", "index": 0 } ]]
    },
    "OpenAI Chat Model (triage)": {
      "ai_languageModel": [[ { "node": "triage_agent", "type": "ai_languageModel", "index": 0 } ]]
    }
  }
}

Read it slowly, because the entire pattern is in those seven entries:

  • billing_specialist connects out via ai_tool toward triage_agent. It's an orchestrator tool.
  • lookup_charge, get_customer_profile, and open_dispute connect out via ai_tool toward billing_specialist. They're the specialist's tools.
  • The same class of connection, ai_tool, shows up at two levels. That's the nesting, written into the graph.
  • Postgres Chat Memory goes to triage_agent and nobody else — lesson 3's ownership rule, made into wire.
  • There are two different model nodes, one per agent.

What to expect. You send through the chat: "Hi, there's a $1,200 charge on my card I don't recognize."

In the execution panel you're going to see the triage_agent node run, and inside its detail a call to the billing_specialist tool. If you open that call, you see the task the orchestrator drafted — something like "Customer reports a $1,200 charge they don't recognize, on the card on file. Customer C-9931. Check whether it matches any purchase and, if not, open a dispute." Notice the orchestrator wrote that assignment: it didn't forward the customer's message as-is, it translated it, because that's what the $fromAI("task", ...)'s description asked for.

Then you see the billing_specialist node run on its own, with its own calls to lookup_charge, get_customer_profile, and open_dispute in sequence. And finally the triage_agent again, composing the response that reaches the chat.

What you're not going to see anywhere: a Switch node, an IF node, or any condition you wrote saying "if the message talks about a charge, go this way." The decision to call billing_specialist was made by the orchestrator's model comparing what the customer asked for against the specialist's Description. It's exactly the same mechanism an agent uses to choose between lookup_order and send_email — except the chosen tool turned out to be another agent.

The second mechanism: an agent inside a sub-workflow

There's a second native way to delegate, and it's worth knowing because in some cases it's the better one. It's the one left announced at the end of Module 4's lesson 6: a sub-workflow that has an AI Agent inside it, exposed to the orchestrator with Call n8n Workflow Tool.

The setup is this:

# Separate workflow: "Billing Specialist"
Execute Sub-workflow Trigger        ← Input Source: Define Using Fields Below
  (field: task, type String)              declares the input contract
  → AI Agent (with its model, its prompt, and its billing tools)
  → Edit Fields (Set)               ← last node: defines what gets returned
# In the orchestrator's workflow
Call n8n Workflow Tool
  Description: "Resolves charge cases, unrecognized charges..."
  Source: Database
  Workflow: "Billing Specialist"
  Workflow Inputs:
    task = {{ $fromAI("task", "Complete, self-contained assignment...", "string") }}

From the orchestrator's point of view, this is indistinguishable from AI Agent Tool: a tool with a description and a parameter. The difference is on the other end of the wire.

Which one to use

AI Agent ToolSub-workflow with an agent inside
Where the specialist livesIn the same workflow as the orchestratorIn a separate workflow
Canvas complexityEverything together: you see the whole system at a glance, and it fills up fastThe orchestrator stays clean; the detail lives elsewhere
Reuse across systemsOnly within that workflowHigh: several orchestrators can call the same specialist
Testing it in isolationHard: you have to trigger the orchestratorEasy: you run the sub-workflow with a test task
Input contractThe $fromAI()'s descriptionDeclared with fields and types in the trigger
Steps before or after the agentNo room to put themYes: you can validate the input, normalize the output, log it
Execution traceNested inside the same executionShows up as a sub-workflow execution, linked
OverheadLowerOne extra execution hop

The practical rule. Start with AI Agent Tool: it's more direct, everything shows up on one canvas, and for a three-specialist system it's plenty. Move to a sub-workflow when one of these three things comes up:

  1. More than one system needs the same specialist. The billing specialist that handles the web chat is also useful to the finance team's internal Slack agent. One single place to fix it.
  2. You want to test it in isolation. Being able to send Billing Specialist twenty test assignments and review its twenty responses, without going through the orchestrator, is enormously valuable while you're tuning its prompt.
  3. You need steps around the agent. Validating that the task carries the minimum fields before spending a call to the model; normalizing the output into a fixed shape; writing a line into an audit log. All of that needs nodes before and after the agent, and that requires a workflow.

The two forms can be mixed within the same orchestrator without a problem: two specialists as AI Agent Tool and one as a sub-workflow, if that's what fits.

Why not a Switch: the two designs side by side

We already said it in lesson 1 as a warning. Now that you have the real wiring, it's worth putting them side by side, because the differences stop being abstract.

# OLD DESIGN — simulated with Switch
Chat Trigger
  → Basic LLM Chain  ("classify this message: billing | orders | sales")
  → Switch (3 fixed branches on the returned text)
      ├── billing branch → AI Agent with billing tools → now what?
      ├── orders branch  → AI Agent with orders tools     → now what?
      └── sales branch   → AI Agent with sales tools      → now what?
# NATIVE DESIGN — delegation via ai_tool
Chat Trigger
  → AI Agent (triage_agent)  ← Postgres Chat Memory
       │ ai_tool
       ├── AI Agent Tool: billing_specialist → its 3 tools
       ├── AI Agent Tool: order_specialist   → its 3 tools
       └── AI Agent Tool: sales_specialist   → its 2 tools

Four concrete differences, not stylistic ones:

1. A message with two topics. In the old design, the Switch takes one branch. By construction. A customer asking about a charge and an order gets half a response. In the native design, the orchestrator calls two tools on the same turn and composes.

2. The result comes back. In the old design, once the flow entered the billing branch, there's no natural way for the result to get back to whoever classified it. That "now what?" in the diagram is real: you have to hand-build the way back, and decide what to do if a branch didn't run. In the native design, returning is what a tool does: the result goes back to the agent that called it and that agent keeps reasoning with it.

3. You can decide again. If the orders specialist reports "this case is actually a billing one, the customer is talking about a charge, not a shipment," the orchestrator can read that and delegate to whoever's right on the same turn. In the old design, the Switch already ran and it doesn't run again.

4. Adding a specialist. In the native design: you drag a node, connect it to the Tool port, and add a line to the orchestrator's system prompt. In the old design: you add a branch to the Switch, add the new label to the classifier's prompt, and review the reunification logic you hand-built. The first one is a two-minute operation; the second is a change that risks breaking what already worked.

And once more, the honest precision: none of this says Switch is a bad node. It's excellent at what it does. If the input channel gives you a structured field — {"department": "billing"} from a form — using a model to "decide" something that's already decided is throwing money away. The boundary is this: Switch for data, delegation for language.

Reading the nested trace

When you delegate, your ability to debug depends entirely on being able to read what happened at each level. With Return Intermediate Steps turned on on both — the orchestrator and each specialist — the output has this shape:

triage_agent
├── model call (decides to delegate)
├── tool: billing_specialist
│   └── (inside) billing_specialist
│       ├── model call
│       ├── tool: lookup_charge      → no matches
│       ├── model call
│       ├── tool: get_customer_profile → one card, no aliases
│       ├── model call
│       ├── tool: open_dispute       → D-8842
│       └── model call (produces its result)
├── model call (evaluates the result)
└── final response to the customer

When something goes wrong, that structure tells you which level to look at, which is half the work:

SymptomWhere to lookWhat it usually is
The specialist never gets calledOrchestrator levelIts Description doesn't cover how real customers talk, or the orchestrator's prompt doesn't mention it
The wrong specialist gets calledOrchestrator levelTwo Descriptions that overlap — lesson 2's tool collision, now between agents
The right one gets called but can't resolveThe task it receivedThe assignment arrived incomplete; a piece of data that was in the conversation is missing
It resolves badly even though the assignment was fineInside the specialistIts system prompt, or one of its domain tools
It gets stuck partway with no errorThe specialist's iterationsIt hit its Max Iterations — lesson 6
The final response ignores what the specialist returnedOrchestrator levelIts prompt doesn't tell it what to do with the result, or the specialist's output format is ambiguous

That "what task did it receive" deserves emphasis: it's the single most useful inspection point in the whole system. Half of a multi-agent system's problems aren't in the agents themselves but in what they hand each other, and the task is where that's written in plain text, exactly as the orchestrator's model drafted it. Before touching a specialist's prompt because it "doesn't know how to resolve" something, read the assignment it received. Often the answer is right there.

Common mistakes

Writing a specialist's Description as if it described an action (conceptual). What happens: someone puts Description: "Checks charges and opens disputes" on billing_specialist. The orchestrator starts calling it only when the customer uses words very close to "charge" or "dispute," and lets obvious cases like "I got overcharged" or "this doesn't match what I bought" slip through. Why it happens: it's the reflex from Module 4, where tools really were actions and describing them by their action was correct. How to spot it: gather ten real customer messages from that domain and check how many share vocabulary with your description; if it's three out of ten, the description is too narrow. How to fix it: describe the scope and the symptoms, not the mechanics — "use it when the customer mentions a charged amount, a card, an account statement, or says something doesn't add up in what they were charged" — and always add the "don't use it for…" that sets it apart from its neighbors.

Passing the specialist the customer's raw message instead of an assignment (conceptual). What happens: the $fromAI("task", ...) gets defined as "the customer's message," and the specialist receives "hi, hey, something weird showed up on my card and also I wanted to ask about my order." The specialist doesn't have the customer's ID (it was in the system, not in the message), doesn't have the amount (the customer said it two turns ago), and also tries to resolve the order thing, which isn't its domain. Why it happens: forwarding the message as-is is the simplest thing to do and seems like the most faithful one. How to spot it: read the task that arrived in the trace; if it looks like how a customer writes rather than how an internal assignment is drafted, that's the problem. How to fix it: the $fromAI()'s description has to require a self-contained assignment, with the data already extracted and with a single topic — and the orchestrator's system prompt has to repeat that rule, because a single mention rarely holds.

Leaving the same expensive model on all five agents (practical). What happens: someone connects the most capable model available to all five nodes, and the per-conversation cost multiplies without quality improving proportionally — the orchestrator is making a three-option decision with a model meant for complex reasoning. Why it happens: the default model is whatever was already configured, and changing it agent by agent feels like premature micro-optimization. How to spot it: look at the trace and classify each model call by how hard the decision is; the orchestrator's are usually "choose between three well-described options." How to fix it: fast, cheap model in the orchestrator, capable model in the specialists that make costly decisions, mid-tier model in the rest — lesson 7 puts the numbers on this lever.

Connecting a specialist and not mentioning it in the orchestrator's system prompt (practical). What happens: the tool is connected, the wire shows on the canvas, and the orchestrator almost never calls it. Why it happens: technically the model sees the tool and its description even if the prompt doesn't name it, so "it should work" — and sometimes it works halfway, which is worse than not working, because it looks like a random problem. How to spot it: count across twenty executions how many times each specialist got called; if one has zero or almost zero calls in cases where it clearly applied, this is why. How to fix it: explicitly declare the roster in the orchestrator's system prompt, with one line per specialist and its scope — the model chooses better when the prompt confirms the policy, not only when the tool is available.

Connecting memory to specialists out of habit (practical). What happens: while building each AI Agent Tool, your hand goes on autopilot and connects a memory node "because that's how you build an agent." The three problems from lesson 3 show up, plus the per-conversation cost rises with no visible explanation. How to spot it: check the exported JSON and count how many ai_memory entries there are; there should be exactly one, pointing at the orchestrator. How to fix it: leave the specialist's Memory port empty and make sure the task carries what it needs — if a specialist genuinely needs its own thread, that's the exception, and it needs to be justified in writing.

Exercises

Exercise 1 — Read the graph. You get handed this connections fragment from a workflow and told it's a multi-agent system with native delegation. Say whether it is, and if not, what's wrong:

{
  "connections": {
    "billing_specialist": {
      "main": [[ { "node": "triage_agent", "type": "main", "index": 0 } ]]
    },
    "lookup_charge": {
      "ai_tool": [[ { "node": "billing_specialist", "type": "ai_tool", "index": 0 } ]]
    }
  }
}
See solution

It isn't. The error is in the first entry: billing_specialist connects to triage_agent via "main", not "ai_tool". That means the two agents are chained into the normal data flow — one runs after the other, always, in a fixed order — instead of one being a tool of the other.

The practical consequences: billing_specialist runs on every execution, even if the message has nothing to do with billing; triage_agent can't decide whether to call it or not; and there's no way for the orchestrator to reason about the result and decide to delegate to another specialist, because the sequence is already fixed by the wire.

It's, essentially, a fixed chain (lesson 3's Form 1) disguised as a multi-agent system. The fix is deleting that main connection and dragging billing_specialist's output to triage_agent's Tool port, which creates a connection of type ai_tool.

Why it works: the connection type is what defines the relationship between two agents. main is "after"; ai_tool is "available for you to decide to use." Reading the exported JSON is the fastest way to verify which of the two you actually built.

Exercise 2 — Write the Description and the $fromAI(). TuTienda wants to add a fourth specialist: warranty_specialist, which handles product warranties — claims for failures within the warranty period, processing repairs, and replacements for manufacturing defects. order_specialist (orders, shipments, remorse returns) and billing_specialist already exist. Write the AI Agent Tool node's Description and the prompt field's $fromAI() expression.

See solution
Description:
  Resolves warranty claims: products that failed or stopped
  working within their warranty period, processing repairs, and
  replacements for manufacturing defects. Use it when the
  customer says something broke, stopped working, arrived defective,
  or asks about warranty coverage.
  Do NOT use it for remorse returns — when the customer simply
  didn't want the product — or for orders that arrived damaged in
  shipping: those two cases belong to order_specialist.
  Do NOT use it for charge complaints: that's billing_specialist.
{{ $fromAI(
     "task",
     "Self-contained assignment for the warranty specialist. It must
      include: what the product is (name or SKU), when it was
      purchased, what failure the customer describes, and the
      customer's ID. This specialist does NOT have access to the
      conversation history: any data mentioned in earlier turns
      must be written here. If any of that data is missing, write
      it in anyway noting which one is missing, so the specialist
      can report it.",
     "string"
   ) }}

What the Description does well: it describes symptoms in the customer's vocabulary ("broke," "stopped working," "arrived defective"), not internal mechanics; and it draws two explicit boundaries against the two neighbors it would most easily get confused with. The boundary with order_specialist is the trickiest one because "the product is damaged" fits both depending on when the damage occurred — which is exactly why it needs to be said, not left implicit.

What the $fromAI() does well: it lists the minimum fields instead of saying "all relevant information," which is too vague for the model to comply with consistently; and it tells it what to do when a piece of data is missing, instead of letting it make one up.

Why it works: the Description is for the caller — it has to recognize the case — and the $fromAI() is for the assignment — it has to carry the data. They're two texts with two different recipients, and confusing them is one of the most frequent mistakes when building your first system.

Exercise 3 — Choose the mechanism. For each of these three cases, decide whether AI Agent Tool or a sub-workflow with an agent inside is the better fit, and justify it:

(a) A sales_specialist that only serves TuTienda's web chat, with two tools, still under construction and with the prompt changing every day. (b) An escalation_specialist that decides whether a case goes to a human, and that's going to be called by the web chat agent, the WhatsApp agent, and the finance team's internal Slack agent. (c) A refund_specialist whose output always has to be logged to an audit table, with a timestamp, before being returned to the orchestrator.

See solution

(a) AI Agent Tool. A single consumer, few tools, and a prompt changing daily — having it on the same canvas makes each iteration a matter of opening the node and editing. Moving this to a sub-workflow would add a navigation hop to every change, in exchange for a reuse that doesn't exist yet.

(b) Sub-workflow. Three different consumers is exactly the reuse case: if the escalation policy changes, it gets fixed in one single place and all three systems stay up to date. If it were an AI Agent Tool, there'd be three copies of the same agent, and sooner or later two of them fall out of sync.

(c) Sub-workflow. The requirement to log to audit before returning needs a node after the agent and before the return, and an AI Agent Tool has nowhere to put that node. In the sub-workflow, it goes between the AI Agent and the final node that defines the response — being careful, as you learned in Module 4's lesson 6, that the audit-logging node doesn't end up at the end of the chain, because then what gets returned would be the logging step's output and not the agent's result.

Why it works: the table's three criteria — reuse, isolated testing, steps around it — cover the vast majority of real decisions. If none of them applies, AI Agent Tool is the default choice for simplicity.

Summary and next step

You now know how to delegate natively. The AI Agent Tool node is a complete agent — with its own Chat Model, its own tools, and its own system prompt — that connects to another agent's ai_tool port, the same way you connected a Gmail node in Module 4. Its Description tells the caller when to use it, describing a scope and not an action; the assignment travels in a $fromAI("task", …) whose description must require it to be self-contained; and its Options carry its system prompt, its Max Iterations, and the trace. The second mechanism — an agent inside a sub-workflow, exposed with Call n8n Workflow Tool — is the one worth reaching for when the specialist gets reused, when you want to test it in isolation, or when you need steps around it. And in the exported JSON, the pattern's signature is the same class of connection, ai_tool, showing up at two levels.

Before moving on you should be able to: connect an AI Agent Tool to an agent's Tool port and explain why that connection is ai_tool and not main; write a scope-level Description with its explicit boundary against neighboring specialists; write a $fromAI("task", …) that produces self-contained assignments; and read a nested trace to know at which level a problem is.

What's still loose is what they hand each other. So far, the assignment is free text and the specialist's response is too. That works in the demo and breaks the moment the system grows: the orchestrator has to guess, by reading prose, whether the case got resolved, whether a piece of data is missing, or whether a human is needed. Lesson 5 turns that exchange into an explicit contract — what goes in, what comes out, with what fields, and what happens when the specialist can't resolve something — which is the same discipline from Module 4's tool contract, applied between agents.

Resources