Module 8: Project: Multichannel Customer Support System

8. Delivery: demo, defense, and how to present it in your portfolio

Description

By the end of this lesson you're going to have the project packaged and defensible: the six artifacts consolidated in one place, a recorded three-minute demo with its timed script, a portfolio README someone can read without you in the room, and the written answers to the eight questions you're going to get asked — including the one most people fail, which isn't technical.

This matters because there's an uncomfortable asymmetry in this work: the system you built is worth what you can demonstrate about it. An excellent project nobody understands in three minutes performs worse than a modest one presented well. And the difference isn't charisma or marketing — it's having the right artifacts and having decided beforehand what to show and in what order.

There's something worth saying plainly before we start. Almost everyone presents an AI system the same way: opens the chat, types a question, the bot answers well, and explains the architecture. That proves the system works, and "it works" is the part whoever's evaluating takes for granted — if it didn't work, you wouldn't be showing it. What nobody shows, and what decides the conversation, is what happens when someone tries to break it, and what you know about your own limits. This entire lesson revolves around that.

Connection to the module: this lesson builds nothing. It consolidates. Lesson 1 defined the evaluation criteria; today you check them off. Lessons 2 through 7 produced the artifacts — the paper design, the matrix, the policy, the battery, the cost sheet, the audit log; today they come together. And several of the defense questions already have their answer written in the trade-offs you documented in lesson 2, which is exactly why that lesson insisted so much on writing them down.

The plate that goes to the table

We've used the kitchen three times in this module, so let's close there.

A cook can spend months perfecting a dish. Adjusts the doneness, tries three fish suppliers, calibrates the sauce. All that work is real and none of it shows up on the table. What comes out is a plate, in one minute of attention from whoever's about to eat it.

And here's what separates a cook from a cook who gets the job. When the critic asks "where does the fish come from?" there are two possible answers. One is "our usual supplier" — honest, and says nothing. The other is "from a market on the coast, it arrives Tuesdays and Fridays; I tried three suppliers and this is the only one that gives me a whole piece, which is what I need for the doneness I'm after; on Thursdays the dish isn't on the menu." The second answer isn't longer for decoration: it contains a decision, a comparison, and a recognized limit.

That's exactly the structure of a good interview answer about your project. Decision, comparison against what you discarded, and the limit you acknowledge. All three parts, always. And the third is what carries the most signal, because it's the only one that can't be improvised.

The other thing the cook does: doesn't explain the dish before serving it. Serves it, and explains around what the person is tasting. Your demo works the same way — the attack goes at second twenty, not the architecture.

Let's package it up.

Phase 1 — The delivery package

Six artifacts, in one place. If they're scattered across notes from seven lessons, they don't exist.

tutienda-project/
├── README.md                    ← what whoever evaluates you reads
├── demo.mp4                     ← 3 minutes (or the link)
├── workflows/
│   ├── wf_agent_core.json
│   ├── wf_channel_web.json
│   ├── wf_channel_whatsapp.json
│   ├── wf_tool_return_eligibility.json
│   ├── wf_tool_escalate.json
│   └── wf_tool_issue_refund.json
├── docs/
│   ├── 01-design.md             ← lesson 2
│   ├── 02-role-sheets.md        ← lesson 3
│   ├── 03-permissions-matrix.md ← lessons 2 and 4, with the L3 section
│   ├── 04-approval-policy.md    ← lesson 6
│   ├── 05-case-battery.md       ← the 12, with the before/after table
│   ├── 06-cost-sheet.md         ← lesson 7
│   └── 07-limitations.md        ← what it does NOT do, and why
├── sql/
│   ├── schema.sql               ← views, users, GRANT
│   ├── seed.sql                 ← sample data + KB + identities
│   └── detection.sql            ← the three queries
└── defense.md                   ← your answers to the 8 questions

Before exporting the workflows, check two things. That no credential ends up inside the JSON — n8n exports references, not secrets, but it's worth verifying by opening the file — and that no real customer data is left in the examples. A portfolio leaking a real phone number is a portfolio that disqualifies itself.

And the file most people skip: 07-limitations.md. It's the list of what your system doesn't do, with the decision behind why. You'll understand why it matters when you reach phase 4.

Phase 2 — The three-minute demo

Three minutes is the right format, and not as a trend: it's what someone watches without pausing, it's what fits in a message, and it forces you to decide what actually matters.

What to record and with what

Screen and voice. You don't need to appear yourself. Any screen recorder works; what matters is that the on-screen text is readable, so turn up the browser's zoom before recording — an n8n canvas at normal size is illegible in a compressed video.

Prepare three windows and have them ready: TuTienda's chat, n8n's Logs view next to it, and the channel where approvals arrive. You're going to switch between them and there's no time to go looking for anything.

Rehearse with a clock before recording. The first attempt always runs five minutes, and not because there's too much content: because you over-explain. The second take usually comes out right.

The script

0:00–0:15 — The system in one sentence
  "This is an online store's customer support system: a triage
   agent delegating to two specialists, serving both web chat
   and WhatsApp, with shared memory between the two channels.
   What I want to show you isn't that it works, but what
   happens when someone tries to break it."

  ▸ On screen: the one-page design. Two seconds, no more.

0:15–0:50 — The attack, live
  Paste the social-engineering message — the fake quality
  engineer — into the chat, with the Logs view open alongside.
  DON'T use the [SYSTEM OVERRIDE] block: it looks like an
  attack, and whoever's evaluating assumes any filter would
  catch it.

  "Notice it doesn't have any suspicious phrase. It's a
   message any filter would let through, and rightly so."

0:50–1:30 — Where it gets stopped
  Point out in the trace the moment the specialist decides to
  call issue_refund. Switch to the approvals window.

  "The model got convinced. It happened. And here's what I
   wanted to show you: the request says reason 'sandbox
   verification,' below is the customer message that triggered
   it, and it says how the customer's identity was verified.
   I, from outside the conversation, can see that doesn't add
   up."

  Deny it. Go back to the chat. Show the agent doesn't retry
  and doesn't promise anything.

1:30–2:00 — The layer that doesn't depend on anyone
  Open the permissions matrix, L3 section.

  "This is what I do when neither the filter nor the person is
   enough. The agent doesn't have any tool that cancels
   orders, updates prices, or emails a recipient it decides on.
   And its queries run under a database user that only has
   SELECT on three views that don't expose addresses or card
   data."

2:00–2:30 — Multichannel, in ten seconds
  Write over WhatsApp from the phone. Then continue the same
  conversation from the web chat.

  "Same customer, two channels, one single conversation. The
   memory key is the same because both identities resolve to
   the same customer. It's one brain with two five-node
   adapters."

2:30–2:50 — The numbers
  Show the cost sheet and the before/after table.

  "Twelve test cases, run before and after hardening. The
   legitimate customers keep getting served the same way —
   that was the hard requirement. And each conversation costs
   between three and eight cents of a dollar in model spend;
   the whole system runs around a hundred dollars a month for
   two thousand conversations."

2:50–3:00 — The honest close
  "What I can't tell you is that it's immune. Two out of every
   five attempts still convince the model, and nobody solves
   that today. What I can tell you is that convincing it
   stopped being enough."

Five decisions in that script worth understanding, because they're transferable to any technical demo.

The attack goes at second fifteen. No architecture introduction. The hook is the attack, and everything else gets explained around it. If you start by explaining the diagram, you lose whoever's watching before the one-minute mark.

The attack chosen doesn't look like an attack. The [SYSTEM OVERRIDE] block is more spectacular and less convincing: it looks weird, and whoever's evaluating thinks any filter would catch it. The fake quality engineer looks like a customer, and that's the entire point.

The L3 matrix gets shown. It's the artifact fewest people bring to an interview and the one that says the most about how you think. Documenting what the system can't do turns a hunch into a verifiable guarantee.

The numbers come at the end and are concrete. Not "it's cheap": three to eight cents, a hundred dollars a month, two thousand conversations. A number invites a question; an adjective doesn't.

It closes acknowledging the limit. It's counterintuitive to end with a weakness, and it's what makes everything before it credible. Whoever's evaluating knows prompt injection isn't solved; hearing someone claim otherwise invalidates the rest of what they said.

Phase 3 — The portfolio README

The document someone reads without you in the room. It has to answer, in this order: what it is, what problem it solves, how it's built, what decisions you made, what you measured, and what it doesn't do.

# Multichannel customer support system — TuTienda

AI agent system for an online store's customer support, built on
self-hosted n8n. Serves web chat and WhatsApp with a single
brain, queries the CRM and a knowledge base, creates tickets,
opens disputes, and requires human approval before moving money.

**Demo (3 min):** [link]
**Stack:** n8n 2.0 Community (self-hosted) · Postgres ·
WhatsApp Business API · a cheap model for triage and a capable
one for the specialists.

## What it solves

An online store gets about 300 daily inquiries across two
channels, mostly about order status, unrecognized charges, and
returns. The system resolves most without human involvement,
escalates the ones it can't, and never moves money without a
person approving it.

## Architecture

[diagram of the one-page design]

Three agents: a `triage_agent` that classifies and delegates,
and two specialists — `order_specialist` and
`billing_specialist` — connected as tools. Channels are thin
five-node workflows speaking an eight-field contract with a
single core (`wf_agent_core`). Memory is grouped by customer,
not by channel, so a customer who starts on WhatsApp continues
on the web without repeating context.

## Design decisions

**Multi-agent instead of a single agent.** A prompt containing
both the return rules and the billing rules produces domain
contamination, and no amount of instructions stabilizes it.
Also, with the split, a message about shipping has no physical
route to `issue_refund`.

**Human-in-the-loop only on refunds, and only above $150.** The
threshold was calculated against the real distribution of
refunds and the team's capacity (two people, ten daily
approvals). It leaves 70% of cases with no friction and
concentrates human attention on nine decisions a day. With
aggregate caps of $1,500 daily to cover the automatic tier.

**The return policy lives in code, not the prompt.**
`check_return_eligibility` is a sub-workflow with a
deterministic `Code` node: two runs with the same data give the
same result, it costs zero tokens, and the policy can be
audited by reading fourteen lines.

**Permissions by capability, not by instruction.** The agent
runs under a Postgres user that only has `SELECT` on three views
that don't expose addresses, phone numbers, or card data, and a
second user that can only `INSERT` into three tables. There's no
free-form query operation.

## What I measured

| Metric | Value |
|---|---|
| Cost per typical conversation | ≈ $0.026 USD |
| Cost p90 (two topics) | ≈ $0.075 USD |
| Monthly cost, 2,000 conversations | ≈ $90–100 USD |
| Typical / p90 latency | 9 s / 21 s |
| Test cases | 12, run before and after |
| Attacks that convince the model | 2 of 5 (unchanged after hardening) |
| Attacks that manage to move money | 0 of 5 |
| False positives on legitimate customers | 0 of 3 |

## Security

Six layers, four of which don't depend on the model deciding
well: database permissions, fixed identity parameters,
deterministic output validation, and human approval. The other
two — input guardrail and system-prompt framing — reduce
volume, they don't guarantee anything.

**The worst the system can do:** issue an automatic refund of up
to $150 to an identified customer with no prior refunds in 90
days, for an amount not exceeding their order total, with an
aggregate cap of $1,500 daily. Between $150 and $800, only with
approval from a person who sees the amount, the reason, the
verified identity, and the original message. Above $800, it
can't.

## Known limitations

- **Concurrency.** Three consecutive messages from the same
  customer trigger three executions reading and writing the
  same memory. Solving it requires a queue; it's out of scope.
- **Shared phone.** Two people from the same household writing
  from the same number share conversation history. The tools
  protect them from seeing each other's orders; memory doesn't.
  Mitigated with a disambiguation instruction, not solved.
- **WhatsApp's 24-hour window.** If the team answers an
  escalated case the next day, the response requires a
  Meta-approved template, with its cost.
- **Lexical knowledge base.** Search is text-based, not
  semantic. It works with 20 articles; with 2,000 a different
  approach would be needed.

## How to reproduce it

1. `sql/schema.sql` and `sql/seed.sql` on a Postgres database.
2. Import the six workflows from `workflows/`.
3. Configure credentials (two Postgres, two WhatsApp, one model
   provider, one for the approval channel).
4. Populate `channel_identities` with your own number.
5. Run the battery from `docs/05-case-battery.md`.

Three things about that README worth more than they look.

The limitations section exists and is specific. It doesn't say "scalability could be improved": it says what fails, in what case, and why it was decided not to cover it. A decided gap is a limitation; an unseen gap is a bug waiting to appear, and whoever's evaluating knows how to tell them apart.

The numbers are in a table, up top, with no searching. Someone reviewing twenty portfolios in an afternoon isn't going to read your prose. They're going to look at the table, and if it has measured numbers, they stop.

The phrase "the worst the system can do" is written, literally. It's the answer to the question that decides everything, and having it in the README means whoever interviews you has already read it before asking. That changes the tone of the whole conversation.

Phase 4 — The defense

Eight questions. The answers go in writing, in your own words, in defense.md. Not to read them aloud — that shows — but because writing an answer organizes it, and an organized answer can be said from memory.

The template for all of them: decision, comparison against what got discarded, acknowledged limit.

1. Why multi-agent instead of a single agent with all the tools?

"For two reasons, and one is stronger than the other. The first is about precision: when I tried a single agent with all eight tools, the prompt had to contain both the return rules and the billing rules at once, and domain contamination showed up — the agent would apply a return deadline to a charge, or try to open a dispute over an order in transit. It can be reduced with instructions, but it doesn't stabilize.

The second is what actually decided it: with the split, a message about shipping has no physical route to issue_refund. It's not that the prompt forbids it; it's that the tool isn't connected to that agent. That difference — a structural restriction instead of a textual one — is what keeps working when someone convinces the model.

And the decision's cost: about four more model calls per conversation with delegation, and about eight more seconds of latency. I offset it by putting the orchestrator on a cheap model, because its job is choosing between two well-described options and composing text, not reasoning about money. With that, multi-agent costs about the same as the monolith and fails less.

That said, if the case were a single domain — orders only, no billing — I wouldn't have made it multi-agent. The split is justified with evidence of contamination, not by default."

2. Why human-in-the-loop on refunds and not on everything?

"Because a barrier crossed without reading isn't a barrier. This case's support team is two people with capacity for about ten approvals a day. If I send refunds, disputes, tickets, and escalations all to approval, that's thirty-five daily requests, and past the fifth nobody reads them. The system would look just as secure in the diagram and would protect nothing — that's this layer's own failure mode and it's called approval fatigue.

So I put the threshold where the distribution called for it. Of the forty daily refunds, twenty-eight are under $150: those go automatic, with mandatory logging and an aggregate cap of $1,500 a day across the whole system in case someone discovers the threshold and tries ten $149 ones. Nine are between $150 and $800: those go to a person, and nine fits within a budget of ten. And the three above $800 aren't an agent action: they get escalated, and the team handles them in their normal work flow instead of as a button interruption.

Everything else — tickets, disputes — is reversible with one click, so there the right defense isn't a person: it's tightened permissions and logging."

3. How much does a conversation cost?

"Between 2.6 and 7.5 cents of a dollar in model spend, depending on whether the customer brings one or two topics. I measured it over twenty executions from n8n's panel: for a simple conversation it's seven model calls, about 18,600 input tokens and 720 output, split between a cheap model for the guardrail and triage and a capable one for the specialist. With two thousand conversations a month and a seventy-thirty mix, that's about eighty dollars a month in model spend.

The channel is the other half and behaves differently. Web chat costs nothing. On WhatsApp, this system only has service conversations — the customer starts them and they get answered within the twenty-four-hour window — which fall under a different pricing category than business-initiated templates, and Meta has changed that scheme several times, so I check it against their current table before giving a number. What's expensive on WhatsApp is campaigns, which this system doesn't do.

Defensible total: around a hundred dollars a month, including the server. And the frame I compare it against: two thousand conversations at five minutes each would be a hundred and sixty-six hours of a person's time.

And I know which lever moves that number. Sixty percent of the savings comes from a single decision: the orchestrator runs on the cheap model. It's the agent consuming the most input tokens, because it carries the full conversation's memory; if it were on the capable model, that component would cost almost ten times more."

4. What's the worst thing your system can do?

"Issue an automatic refund of up to $150 to an already-identified customer with no refunds in the last ninety days, for an amount not exceeding their order total, with an aggregate cap of $1,500 daily across the whole system. Between $150 and $800 it can only do it if a person approves it after seeing the amount, the reason the agent gave, how the customer's identity was verified, and the original message that triggered it. Above $800 it doesn't have the capability: it escalates.

Everything else it does is reading data for a customer who's already identified, over views that don't expose address, phone, email, or card data, or writing tickets, disputes, and escalations the team can revert with one click. It can't cancel orders, can't change addresses, can't modify prices, can't email a recipient of its own choosing, and its database credential has no UPDATE or DELETE on any table."

5. How do you avoid prompt injection?

"I don't avoid it, and I think anyone who says they do didn't test it. What I do is make convincing the model stop being enough.

I have the measurement: with fifteen manipulation attempts of three different types, five got the agent to decide to call issue_refund. After hardening the system, those five still convince the model at the same rate — it didn't drop. What changed is that none of them manage to move money, because there are four layers underneath that don't depend on the model deciding well: the credential can't write where it shouldn't, every query's customer_id doesn't come from the model but from the channel's contract, output gets validated field by field against what the tools returned, and the refund goes through a person.

The input guardrail exists and I have it deliberately calibrated above what would catch every attack, because I calibrated it against the legitimate cases: an angry customer typing in caps demanding their money has to get through, and it has no layer underneath to rescue it if I block it. The attacks do have those layers.

The gap I do still have open and have documented: these layers protect actions, not claims. A message that doesn't ask to execute anything and only asks the agent to confirm something false in writing doesn't trigger any barrier. That gets partially covered by output validation, with a refund_status field that can only say 'approved' if the tool ran successfully in that execution."

6. When is an agent NOT the right solution?

"When the decision is deterministic. And I have a concrete case from this same project: return eligibility.

At first it was the agent's judgment — it looked at the delivery date, the category, the deadline, and decided. It worked almost always. The problem is 'almost always' isn't acceptable when the result denies a return to someone who did have the right to one, and on top of it the error is invisible: the response sounds perfectly reasonable. I pulled it out into a sub-workflow with a fourteen-line Code node. Now two runs with the same data give the same result, it costs zero tokens instead of two to four model calls each time, and the company's return policy can be audited by reading code instead of a prompt.

The general rule I use: if the result has to be the same given the same data, it can't live in the model. The agent is for what genuinely requires interpreting language or choosing between paths — understanding what the customer's asking, deciding who to delegate to, drafting the response. A mature multi-agent system usually ends up with fewer agents than it started with and more sub-workflows.

And there's a second case: when there's a hard latency constraint. In voice, every delegation adds a full round, and if the budget is two seconds, delegation doesn't fit."

7. How do you know it works?

"With twelve test cases written before building the system, run before and after hardening it, with the results table. And the part that matters: three of those twelve are difficult legitimate cases — a furious customer typing in caps, one quoting a suspicious instruction they were told, and a refund that genuinely applies — and the success criterion is that they don't change between before and after. If after six security layers my real customers stopped getting served, I didn't harden the system: I broke it.

I wrote them beforehand on purpose, in the design phase, because once the system already works, you test what you know works. Nobody spontaneously invents the anonymous-visitor case while admiring their own chatbot answering nicely.

And there are two things that run on their own. A deterministic validation comparing what the agent claims against what the tools returned, field by field — I measured that in two out of ten runs of a simple query the agent estimated a delivery date no tool had given, and the validator catches it. And three daily queries over an audit log of my own, because n8n's executions get purged after fourteen days and incidents show up later. One of those queries tells me whether my layers are alive: a defense that never reports anything is indistinguishable from a defense that's off."

8. What would you do differently for 50,000 conversations a month?

"Three things, in this order.

First, concurrency, which today is my most serious known limitation. Three consecutive messages from the same customer trigger three executions reading and writing the same memory. At three hundred daily conversations it happens rarely; at fifty thousand a month it happens all the time. It gets solved with a queue and message grouping by session, and it's infrastructure work, not agent work.

Second, cost would stop being a minor detail. At this volume it'd be about two thousand dollars a month, and that's where what doesn't pay off today starts to: caching the knowledge base's responses, which are the same twenty answers repeated thousands of times; and measuring whether the order specialist can run on the cheap model, because its decisions are simpler than billing's. Both are measurements, not hunches.

And third, human approval doesn't scale as it stands. Nine daily requests with two people works; two hundred twenty-five doesn't. At that volume I'd need to raise the automatic threshold backed by data — what percentage of manually approved refunds always get approved — and probably move to a sampling model: automatically approve below a higher threshold and review a sample afterward instead of everything beforehand. It's a risk decision, not a technical one, and I'd make it with the business owner, with the numbers in front of us."

The question most people fail

It's none of the eight. It's this:

"What was hardest about this project for you?"

It sounds like small talk and it isn't: it's the question distinguishing someone who built something from someone who followed a tutorial. The answers that don't work are "nothing in particular," "setting up WhatsApp," and "learning n8n." All three are true and none of them says anything about your judgment.

One that does work has this shape: a problem that wasn't technical, the decision you made, and what you learned that you'd apply again. For example:

"Calibrating the guardrail. My instinct was to lower the threshold until it blocked every attack, and when I did, I discovered it also blocked a furious customer typing in caps demanding their money. I spent a while convinced it was a configuration problem, until I realized it was an ordering problem: I was calibrating against the threats when I needed to calibrate against the customers. I raised the threshold accepting that two out of five attacks would pass the filter, because those two have four layers underneath and the angry customer has none. That changed how I think about layered defenses: the top one doesn't have to catch everything, it has to not get in the way."

Write your own. It has to be true, and it has to have actually happened to you.

Honesty as strategy

There's a pattern running through this whole lesson worth stating directly, because it's counterintuitive and it's what works best.

Acknowledging your system's limits makes you more credible, not less. And not for modesty's sake: for a practical reason. Whoever's evaluating you knows prompt injection isn't solved, that an agent system has failure modes, and that no portfolio project scales to millions of conversations. If your presentation doesn't contain a single limitation, either you didn't test the system, or you're hiding something — and both possibilities are worse than the limitation.

Three concrete ways to apply it, and you already have all three built:

The before-and-after table showing the attacks still work. It's a fact that looks like it's working against you and it's the strongest one you have, because it proves you measured instead of assumed.

The README's known-limitations section. Specific, with the decision behind why each one stays out of scope.

The demo's close. "I can't tell you it's immune; I can tell you convincing it stopped being enough."

And the flip side, because honesty isn't self-flagellation: what you did well gets stated with confidence. Database permissions always work, not "almost always." The customer_id filter doesn't depend on the model, period. The return policy is deterministic, no caveats. Humility goes where things are genuinely uncertain; technical facts get stated.

Where to publish it

Three formats, and it's worth having all three because they serve different moments:

The repository. Phase 1's complete package, with the README as its face. It's what you link in your résumé and what someone reviews if you caught their interest.

The three-minute video, standalone. Upload it somewhere you can link to without anyone having to download anything. It's what you send in a message, and it's what most people are going to see of your project.

A short post telling one decision. Not the whole project: one decision, with its measurement. "Why I calibrated my agent's security filter to let attacks through" is a five-hundred-word piece people read, share, and it demonstrates judgment better than a walkthrough of the architecture. The whole project is boring; one decision with a number isn't.

Common mistakes

Recording the demo by explaining the architecture first (practical). What happens: the video opens with the diagram, continues with a walkthrough of the nodes, and by minute one and a half — once you've already lost whoever's watching — the interesting part arrives. Why it happens: it's the order in which you built the system and the order it's in your head. How to spot it: if nothing surprising happens in your video's first thirty seconds, this is it. How to fix it: the attack at second fifteen, and the architecture explained around what's being shown. The order you build in and the order you present in almost never match.

Presenting the system claiming it's immune to prompt injection (conceptual). What happens: someone sees their table with the five attacks stopped and says "the system is protected against prompt injection." Whoever's evaluating — who knows that doesn't exist — hears an impossible claim, and from then on doubts everything else, including the parts that were actually true. Why it happens: the table is genuinely good and it's tempting to sum it up in a strong sentence. How to spot it: if your opening line doesn't contain a single limitation, it's too strong. How to fix it: "I can't promise nobody will convince the model; I can show you that convincing it isn't enough," and then the table, which shows exactly that.

Bringing the project with no measurement at all (practical). What happens: the demo looks good, the architecture's well explained, and when asked how much it costs or how many cases it tested, the answer is an estimate. The conversation cools off immediately, because it just became clear the system never left the "does it work" phase. Why it happens: measuring produces no visible functionality and always seems like it can be done later. How to spot it: if your README has no table of numbers, this is it. How to fix it: lesson 7's cost sheet and lesson 2's case table are two hours of work, and they carry more signal per unit of effort than anything else in the project.

Improvising the defense on interview day (practical). What happens: someone knows their system perfectly and assumes they can explain any decision on the spot. And they can — but the improvised answer comes out disorganized, forgets to mention the alternative they discarded, and doesn't reach the acknowledged limit, which is the part that carries the most signal. Why it happens: writing answers to questions nobody's asked yet feels artificial. How to spot it: try saying your answer to "why multi-agent" out loud, straight through; if it runs past ninety seconds or wanders off on a tangent, it isn't written. How to fix it: the eight answers written in defense.md, each with the decision-comparison-limit template. Not to read them: to have thought them through once, calmly.

Leaving real data in the package (practical). What happens: seed.sql has the real phone number of whoever tested the system, or a demo screenshot shows a real email in the identities table. A portfolio leaking personal data isn't a minor oversight in this specific field: it's exactly the opposite of what the project claims to demonstrate. Why it happens: test data comes from real life because it's the fastest thing to grab. How to spot it: search for your own number and your own email across every file before publishing. How to fix it: example numbers and emails throughout the whole package, and review the video frame by frame in the parts where a table shows.

Exercises

Exercise 1 — Record the demo and cut it to three minutes. Follow the script, time it, and if you go over, cut. Then watch the recording with the sound off and ask yourself whether it's clear what's happening at each moment.

See solution

What almost always needs cutting, in order of frequency:

The explanation of what n8n is. Whoever's evaluating you either knows it or doesn't care. It's twenty seconds buying nothing.

The canvas walkthrough. It's the part that gives the most pride and communicates the least: a canvas with twenty nodes in a video is a smudge. The one-page design says the same thing in two seconds and reads clearly.

The second half of every sentence. When rehearsing, almost every sentence in the script has an explanatory tail you can cut without losing anything. "The model got convinced, it happened, and that's normal because current models are susceptible to this kind of..." → "The model got convinced. It happened."

What the muted-sound test reveals, which is the reason to do it: the moments where you're talking about something not on screen. If at fifty seconds you're explaining human approval and the screen still shows the chat, whoever's watching is searching, not listening. Every claim needs its visible evidence at the same time.

And a detail that improves the result a lot and costs little: pause half a second before every screen change. It gives the person time to finish reading what came before. Technical videos usually go too fast precisely because whoever's recording already knows what's coming.

Why it works: cutting forces you to decide what actually matters, and what's left after cutting is what genuinely defends the project.

Exercise 2 — Have someone evaluate your README without you. Give the README to someone who doesn't know the project — ideally someone technical who doesn't know about agents — and ask them to answer three things: what the system does, the worst thing it can do, and what it doesn't do. Note what they had to ask you.

See solution

Anything they had to ask you is a section that's missing or poorly written. The gaps that show up most:

"What's a triage agent?" The README uses domain vocabulary without defining it. The fix isn't adding a glossary: it's having the architecture section's first sentence describe the behavior before the name — "an agent that reads the message, decides what it's about, and hands it to the right specialist."

"Is this actually running right now or is it a mockup?" It's the most-asked question and the hardest to answer. It deserves an explicit sentence: what genuinely runs — web chat, WhatsApp with Meta's test number, Postgres with sample data — and what's simulated — the payment gateway. Hiding it shows; stating it costs nothing.

"Why is WhatsApp free?" The README says service conversations fall under a different pricing category, and someone reading with no context understands "WhatsApp is free," which is false. One more sentence of precision is worth it.

And the good signal: if your reader could answer all three questions without help, the README works. And if they also asked you something about a design decision — "why don't you cache the knowledge base's responses?" — that's not a gap: it means the document got someone to think about your system, which is the most it can aspire to.

Why it works: you can't evaluate your own README, because you know what you meant to say. The only valid test is someone who wasn't there.

Exercise 3 — Write the post about one decision. Pick a decision from your project and write five hundred words telling it: the problem, what you tried, what you measured, and what you decided. Not the whole project — one decision.

See solution

The ones that work best, and why:

"I calibrated my agent's security filter to let attacks through." It's counterintuitive from the title, and the explanation — that the filter gets calibrated against legitimate customers and not against threats, because the angry customer has no layer underneath and the attack has four — is reasoning most people haven't done.

"I took a business rule out of my agent and put it into fourteen lines of code." Tells the check_return_eligibility decision with its numbers: from two to four model calls per use down to zero, and from "gets it right almost always" to deterministic. It's the answer to "when isn't an agent the solution," in story form.

"What a customer-support agent actually costs." It's the most-read one, because almost nobody publishes numbers. With the cost sheet broken down by component and the finding that sixty percent of the savings comes from a single model decision.

The structure that works, in four paragraphs:

  1. The problem, in a concrete situation. Not "security in agents is important," but "I lowered the filter's threshold until it blocked every attack and blocked a customer demanding their money in caps."
  2. What you tried, with enough detail for someone to be able to repeat it.
  3. The number. Without a number there's no post: there's an opinion.
  4. The decision and its cost. What you chose and what you accepted losing.

And what you should not do: close by asking for opinions or with a motivational line. A piece that ends on the data point ends well.

Why it works: the whole project is too big for someone to read without a prior reason to. One decision with a number is the right size, and it's what makes someone want to see the whole project — which is exactly the order you're after.

Summary and guide close

The project's delivered. You have the six artifacts in a package, with the workflows exported, the reproducible SQL schema, and the seven documents making the system auditable by someone who didn't build it. A three-minute demo opening with an attack at second fifteen, showing where it gets stopped, opening the matrix's L3 section, demonstrating the channel switch in ten seconds, giving three concrete numbers, and closing acknowledging the limit. A README with the measurements table up top, the "worst it can do" sentence written literally, and four known limitations with their decisions. And eight answers written with the decision-comparison-limit template, plus the ninth nobody prepares for.

Looking at the whole module: you started with eight requirements and a three-level rubric. You spent forty-five minutes on a paper design with five decisions and twelve test cases written before anything existed to test. You built the brain with fake tools so you could get it wrong for free. You gave it real hands with trimmed views, read-only credentials, and a business rule moved into deterministic code. You opened two doors onto a single core with per-customer memory and an identities table recording how each one was verified. You put locks on it calibrated against the customers and not against the attacks, with human approval whose threshold came from the real distribution and two people's capacity. You gave it instruments: a deterministic validation, an audit log surviving the purge, three queries that run on their own, and a cost sheet in dollars. And you packaged it so it's understandable without you.

And looking at the whole guide: you started by telling apart an agent from a chatbot and from procedural AI. You gave it a brain with a current model and a system prompt with role and limits. You gave it memory, and learned when that memory degrades and when to let it go. You gave it tools over real systems, with their contracts and their trust boundaries. You split it into a team that delegates natively, with stopping conditions and measured economics. You brought it to the channels where people are, with an architecture that doesn't duplicate logic. You hardened it against whoever tries to hijack it and against its own tendency to claim what it doesn't know. And you put it all together into a system that ships, gets measured, and defends itself.

Not bad for something that started with one node answering one question.

What's next depends on where you're headed. If your path is documentary knowledge — an agent answering questions over your own PDFs, invoices, and manuals with semantic retrieval — that's the ecosystem's next guide, and this system is the base it builds on: search_knowledge_base is exactly where it plugs in. If your path is running this for real — separate environments, external secrets, Git versioning, monitoring with alerts and CI/CD — that's the production and maintenance guide, which picks up right where lesson 7 set its honest limit. And if your path is building agents in code instead of orchestrating them, all of this guide's judgment transfers: the contracts, the permission levels, human approval, and deterministic validation are the same, only where you write them changes.

Whichever it is, you already have what job postings ask for: a real agent system, measured, secure, and defensible. Not a POC.

Resources