More agents is not more intelligence
A multi-agent system is a program where one model call hands work to other model calls, each with its own context. It matters because every agent you add is paid for in tokens, time and new ways to fail.
Teams of agents are sold as the next step. Sometimes they are. Often one agent with good tools does the same job for much less.
By the end of this lesson
Note
I can use token estimates to decide when more than one agent is worth the cost.
Key words
| Word | What it means |
|---|---|
| Context window | the most text one agent can hold at once |
| Orchestrator | the agent that splits the work and joins the answers |
| Worker | an agent that does one part and reports back |
| Shared text | instructions every agent must read again |
| Coupled work | steps where each one needs the result of the last |
| Parallel | parts that can be done at the same time |
The cells students fill in are left blank in this preview.
Every agent reads the shared text again
A lone agent reads its instructions once. A team does not. The orchestrator reads them, and so does each worker, because a worker has a separate context and cannot see anyone else's.
So a team costs the shared text once per agent, plus a brief and a result for every part. The part itself is only read once, by the worker that owns it.
Worked example: three reports
| Who | Reads | Tokens |
|---|---|---|
| One agent | Shared 1500 + three reports 5500 + answer 400 | 7400 |
| Orchestrator | Shared 1500 + three briefs 600 + final 300 | 2400 |
| Three workers | Shared 1500 each + a report each + a result each | 10600 |
| The team in all | Orchestrator and workers together | 13000 |
The cells students fill in are left blank in this preview.
When a split pays for itself
Split when one agent cannot hold the work. Split when the parts are independent and speed matters. Split when a part needs a clean context, so earlier text cannot mislead it.
Do not split coupled work. If step two needs the exact result of step one, a second agent only adds a lossy summary between them.
A common mistake
Warning
Many people split a task because it has several nouns in it. Count the dependencies, not the nouns. Work that shares one record and edits it in order is one agent's job.
Write the model helper, part 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// The token counts of a reply, or null when the reply carries none.' 'function count(data, name) {' ' return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the model helper, part 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' const data = await res.json();' ' const text = data.choices[0].message.content;' ' return { text, inTokens: count(data, \"prompt_tokens\"), outTokens: count(data, \"completion_tokens\"), recorded: false };' ' } catch (error) {' ' console.log(\"Recorded sample: no model answered (\" + error.message + \").\");' ' return { text: sample, inTokens: null, outTokens: null, recorded: true };' ' }' '}' 'module.exports = { ask };' >> model.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the cost script, part 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const WINDOW = 8000; // tokens one agent can hold' 'const NOTE = 200; // one brief or one result' 'const CASES = [' ' { name: \"three reports\", shared: 1500, parts: [1800, 2000, 1700], coupled: false },' ' { name: \"one long report\", shared: 1500, parts: [900], coupled: false },' ' { name: \"edit one file\", shared: 1500, parts: [1200, 1000], coupled: true },' ' { name: \"ten documents\", shared: 1500, parts: Array(10).fill(900), coupled: false },' '];' > split.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the cost script, part 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function sum(list) { return list.reduce(function (a, b) { return a + b; }, 0); }' '// One agent reads the shared text once, then every part, then answers.' 'function single(c) { return c.shared + sum(c.parts) + 400; }' '// A team: the boss reads the shared text again, and so does every worker.' 'function team(c) {' ' const boss = c.shared + NOTE * c.parts.length + 300;' ' const workers = c.parts.map(function (p) { return c.shared + p + NOTE; });' ' return boss + sum(workers);' '}' >> split.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the cost script, part 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function choose(c) {' ' if (c.coupled || c.parts.length < 2) return \"one\";' ' if (single(c) > WINDOW) return \"several\";' ' return team(c) <= 2 * single(c) ? \"several\" : \"one\";' '}' 'async function main() {' ' for (const c of CASES) {' ' const x = Math.round((team(c) * 10) / single(c)) / 10;' ' console.log(c.name + \": one \" + single(c) + \", team \" + team(c) + \" (x\" + x + \"), choose \" + choose(c));' ' }' ' const q = [{ role: \"user\", content: \"In one sentence: name a job that should not be split between agents.\" }];' ' const r = await ask(q, \"Editing one file in steps, because every step needs the last one.\", 40);' ' console.log(\"Model: \" + r.text.trim());' '}' 'main();' >> split.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run the cost script
docker exec -w /home/node/project agent node split.jsShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
These are estimates
The Run does not call a team. It works the sums over four recorded cases, because a real team would spend the lab's allowance in one go. The token sizes are invented, and a real system measures its own.
The one live call only asks for a sentence.
One possible output
| Line | What it shows |
|---|---|
| three reports: one 7400, team 13000 (x1.8), choose several | It fits in one window, but the team is under double |
| one long report: ... choose one | One part cannot be shared out |
| edit one file: ... choose one | Coupled work stays with one agent |
| ten documents: one 10900, team 29800 (x2.7), choose several | One agent cannot hold it, so the cost is paid |
| Model: ... | A sentence that differs every run |
The cells students fill in are left blank in this preview.