Multi-agent demos look clever until three roles share one mutable system prompt and start rewriting each other’s plans. The unfair advantage is typed contracts between Planner, Implementer, and Critic — separate prompts, separate tools, separate write surfaces — so collaboration is orchestration, not soup.
⚡ TL;DR: Give each role a frozen system prompt, an input schema, and an output schema. Planner emits a plan artifact; Implementer may only write under a patch workspace; Critic may only emit
ACCEPT|REJECTwith evidence. Never let agents append to a shared chat blob.
Role contracts beat prompt soup
# agents/contracts.py
from pydantic import BaseModel, Field
from typing import Literal
class PlanStep(BaseModel):
id: str
intent: str
files: list[str] = Field(default_factory=list)
acceptance: str
class PlanArtifact(BaseModel):
goal: str
steps: list[PlanStep]
budget_tokens: int = 50_000
class CriticVerdict(BaseModel):
decision: Literal["ACCEPT", "REJECT"]
step_id: str
evidence: list[str]
required_fix: str | None = None
✅ The Implementer never sees the Critic’s chain-of-thought — only the structured verdict.
Isolate prompts and privileges
| Role | May read | May write | Tools |
|---|---|---|---|
| Planner | ticket + repo map | plan.json |
search, list_files |
| Implementer | plan.json + target files |
patch intents | apply_intent, test |
| Critic | plan + diff + test log | verdict.json |
read_only |
// runtime/roleGate.ts
export function assertTool(role: "planner" | "implementer" | "critic", tool: string) {
const allow: Record<string, Set<string>> = {
planner: new Set(["search", "list_files", "write_plan"]),
implementer: new Set(["apply_intent", "run_tests", "read_file"]),
critic: new Set(["read_file", "read_diff", "write_verdict"]),
};
if (!allow[role].has(tool)) throw new Error(`tool_denied:${role}:${tool}`);
}
❌ One mega-prompt with “you are planner then implementer then critic” — that is how roles collapse mid-run.
Handoff through artifacts, not chat
# ✅ Explicit artifact pipeline
python agents/planner.py --ticket T-441 --out /artifacts/plan.json
python agents/implementer.py --plan /artifacts/plan.json --out /artifacts/diff.json
python agents/critic.py --plan /artifacts/plan.json --diff /artifacts/diff.json --out /artifacts/verdict.json
Store artifacts with immutable keys (run_id/step/plan.json). Auditors replay without reconstructing chat history.
Stop conditions before clever retries
MAX_CRITIC_ROUNDS = 3
def next_action(verdict: CriticVerdict, round_i: int) -> str:
if verdict.decision == "ACCEPT":
return "merge_gate"
if round_i >= MAX_CRITIC_ROUNDS:
return "human_queue" # ✅ escalate, don't ping-pong
return "replan_or_fix"
Closing checklist
- [ ] Separate system prompts per role
- [ ] Schema-validate every handoff artifact
- [ ] Least-privilege tool allowlists
- [ ] Cap critic rounds; escalate to humans
- [ ] Version plan artifacts immutably
Series navigation
Day 50: Project: Internal SDK Autocomplete Model · Day 52: Blackboard vs Message Bus Orchestration
Last updated September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
