JJoeven

Curriculum/Multi-Agent Systems

Roles: Planner, Worker, Critic

Three classic roles with different tools, outputs, and stop conditions. The critic should not hold the worker’s write tools. Start with this trio, not a soap opera.

intermediate21 min6 / 24

If you only ever add extra agents once, add roles with teeth: different tools, different outputs, different definitions of done. The split-or-merge lesson asked for a contract table. This lesson fills the most common honest table: planner, worker, critic.

This is the Agents track’s router / specialist / verifier idea with separate traces. You can eval the planner for coverage (did every constraint get a step?), the worker for tool correctness (did it only call what the step allowed?), the critic for false rejects (did it block a draft that already met the rubric?). One runtime with three functions cannot give you those three traces. Three names with the same tools still cannot. You need the teeth.

The planner should not quietly do the work. If it can run_shell, it will “save a round trip” and you are back to one agent with a hat. Workers return artifacts (files, JSON), not chat. Cap their budget per assignment. The critic is grounded. If tests exist, it reads the test report. A critic that only says “looks good” is a random boolean you could have replaced with return True.

The three contracts

RoleSeesMay doDone when
PlannerGoal, constraints, catalog of workersEmit a plan / assignmentsPlan is valid or approved
WorkerA step plus the tools for that stepCall domain toolsStep predicate is true
CriticWorker output plus evidenceAccept, reject, comment — usually no writesRubric returns pass or a fix list
Three roles with teeth
PlannerWorkerCritic

Different tools, different done checks. The critic does not hold write tools.

Three roles with teeth

Planner. Input: the user goal and a catalog like docs, writer, coder. Output: a list of typed steps, not a pep talk. Each step names a worker, an intent, and later a budget. The planner does not fetch the policy and does not write the user-facing draft. If your planner’s allow-list includes the worker’s writes, the planner will skip the worker when it is “sure.” Sure is how src/app.py gets patched without tests.

Worker. Input: one step from the board, plus only the tools for that step. A docs worker may fetch. A writer worker may draft from an artifact id. A coder worker may edit and run tests. Workers do not pick the next worker. That is orchestration (next part). Workers do not hold the critic’s rubric. They produce an artifact with an id.

Critic. Input: the artifact plus evidence ids (doc id, test report id, policy snippet). Output: {ok, issues[]}. No edit, no refund, no send_email. If issues exist, the runtime reassigns the worker with those issues on the board. If you give the critic writes, you have two workers, and the critique path disappears into extra side effects. The next two lessons lock isolation and the no-write rule. Here you only need the shape.

You can eval them apart. Planner coverage: every required intent appears. Worker: schema of the artifact, tools actually called. Critic: on a fixture that is already correct, it must not invent issues; on a fixture missing 5-7, it must not pass.

Walkthrough: quote the refund delay

Board goal: summarize refund policy with a quote. This is not a coding ticket. It is a small team so you can see the teeth.

  1. Planner emits two steps: docs worker fetches the policy; writer worker quotes the delay in days.
  2. Docs worker writes artifact kb-44 with the sentence about 5-7 business days. It does not write the user draft. Done-check: artifact has a doc string and an id.
  3. Writer worker reads kb-44 from the board, not from chat. It writes a draft with text, quote, and source id. Done-check: those keys exist.
  4. Critic has no tools. It checks: delay 5-7 in the text, source id kb-44, a quote field. Pass or a fix list.

If the writer omits the delay, the critic returns issues and the runtime sends the writer back — it does not let the critic type a new sentence into production. If the planner had fetched the doc itself, you would have no docs artifact to eval, and the writer would depend on planner prose.

Start with this trio on one product surface. Adding intern, intern-2, and manager is how you get a soap opera. A second worker type (coder vs docs) is a new contract, not a new nickname. A supervisor, later, is a planner that only assigns and never chats with workers as peers.

Live PythonOpen full playgroundpython
Output
Run to execute this in your browser. Nothing is sent to a server.

What printed: PLAN is two typed steps, not a paragraph. DOCS is artifact kb-44 with the 5-7 sentence. DRAFT quotes that doc and sets source to kb-44. CRITIC is ok: True with an empty issue list. The last line reminds you the critic has no tools. Break the draft: delete 5-7 from text and run again. The critic should list missing delay. That is a grounded rubric, not a personality.

This toy mutates one board dict in place so you can read it. Production should copy-on-write (blackboard lesson) so a failed critic cannot leave a half-edit. The planner here is a function that always returns the same two steps. A model planner still must emit this shape. If it emits a novel, parse fail-closed like the brief parser.

What this trio is not

It is not ReAct with three hats. ReAct is one policy calling tools in a loop. Here three policies exist, and code (or a supervisor) moves the board. Workers do not “think about who is next” unless you chose peer handoff on purpose, with hop limits.

It is not debate. Debate is two answers plus a judge on a hard, checkable question. A critic with a rubric is cheaper. Use debate later when single-sample workers fail in a known way and you have evidence for the judge.

It is not a swarm. One docs worker and one writer is a pipeline. Fifty writers on the same draft is a race.

How agents use this

Handoffs are typed events (next lessons). If those structs are not in the codebase, you have a group chat with job titles. Name three functions you can grep: plan_ticket, run_step, review_artifact. If a teammate cannot find the critic’s forbid-writes, you do not have a critic.

Eval each role on fixtures that only that role can fail. Planner: omit a required step, expect the plan parser to reject. Worker: illegal tool, expect dispatcher deny. Critic: golden-good draft, expect ok; golden-bad draft, expect a named issue, not a poem.

Cap worker budget per step on the assignment object. A writer that calls search forty times is not “thorough.” It is a furnace inside a specialist. The stop belongs to the worker’s loop and to the team hop cap.

When product asks for intern and manager, show this table. If intern and manager would share the writer’s tools and the writer’s done-check, merge them into the writer. If you need assignment, that is the planner or a supervisor, with no domain writes.

Check your understanding

Why should the critic usually not have write tools?