Roles and Personas: Give Each Agent One Clear Job
Each agent in a team should have one job that you can describe in a single sentence, with a defined input and a defined output. When roles overlap, agents duplicate work, contradict each other or skip steps because each assumes another agent handles them. Good role design is the cheapest way to improve a multi-agent system.
Roles are contracts, not costumes
A common beginner habit is to write "You are a world-class senior researcher with 20 years of experience". Personas like this add little. What matters is the contract: what the agent receives, what it must produce, what it must not do, and when it should stop. Replace flattery with specifics.
A role template
Use the same structure for every agent. It forces you to think about the interface, and it makes prompts easy to review.
ROLE: Source Checker
GOAL: Decide whether each claim in the draft is supported by the provided sources.
INPUT: A draft (text) and a list of sources (text with ids).
OUTPUT: JSON list. One item per claim: {"claim": str, "supported": bool, "source_id": str|null}
RULES:
- Use only the provided sources. Do not use outside knowledge.
- If no source supports a claim, mark supported=false.
- Do not edit the draft.
DONE WHEN: Every claim has an entry.
Notice the explicit "Do not edit the draft" rule. It prevents the checker from drifting into the writer's job.
Common role sets
| Role | Job | Typical tools |
|---|---|---|
| Planner | Break a goal into steps | None |
| Researcher | Gather facts on one subtopic | Search, fetch |
| Writer | Produce text from notes | None |
| Critic | Find problems, never fix them | None |
| Executor | Carry out one action | The action's tool |
| Router | Choose which agent handles a request | None |
Defining roles in code
Keep roles as data so you can version, test and reuse them. A small dataclass is enough.
from dataclasses import dataclass, field
@dataclass(frozen=True)
class Role:
name: str
goal: str
output_format: str
rules: list = field(default_factory=list)
def system_prompt(self) -> str:
rules = "\n".join("- " + r for r in self.rules)
return (
"You are the " + self.name + ".\n"
"Goal: " + self.goal + "\n"
"Output format: " + self.output_format + "\n"
"Rules:\n" + rules
)
CRITIC = Role(
name="critic",
goal="Find concrete problems in the text you are given.",
output_format="A bullet list, or the single word NONE if there are no problems.",
rules=["Never rewrite the text.", "Quote the problem sentence.", "Max 5 bullets."],
)
print(CRITIC.system_prompt())
Printing the prompt shows a short, structured system message. Because the role is an object, you can test that every role has a goal and rules, and diff changes in code review.
Signs your roles are wrong
- Two agents produce nearly the same output when given the same input.
- You cannot say what one agent returns without saying "and also".
- An agent frequently ignores its role and does a neighbour's work.
- The orchestrator has to repair or reformat most outputs.
Practical tips
- Say what the agent must not do. Negative rules prevent role creep.
- Specify the output format exactly, so the next agent can consume it.
- Merge roles that always run together and never disagree. Two agents that share a context and a goal are one agent.
- Split roles when a prompt grows past one screen or mixes conflicting instructions, like "be creative" and "be strictly accurate".
Example: refactoring a vague role
Suppose you start with one agent called "Content Expert" whose prompt says: research the topic, write the article, check the facts and make it engaging. That is four jobs in one prompt. When the output is poor, you cannot tell which job failed. Split it into a researcher that returns bullet notes with source ids, a writer that turns notes into prose without adding new facts, and a fact checker that compares claims in the draft with the notes. Each now has a testable output. If articles contain wrong facts, you inspect the researcher's notes first; if the notes are right and the draft is wrong, the writer is at fault.
Roles and models
Clear roles also let you choose models sensibly. A router that picks between three specialists needs little reasoning and can run on a small, fast model. A planner that must foresee dependencies may deserve a stronger one. When roles are blurry you end up using the strongest model everywhere because you cannot tell which agent needs it.
Versioning roles
Treat role definitions like code. Keep them in files, review changes, and record which version produced which run. When a team's behaviour changes after a prompt edit, you want to be able to see exactly what changed. A later post covers versioning prompts and agent configs in detail.
Comments
No comments yet. Have a question or a tip? Be the first to share it.