criogenai

Multi-agent

Debate

Two agents argue opposite sides; a judge decides.

Two agents take opposite positions and answer each other for a fixed number of rounds; a judge reads the exchange and decides. Each side is pushed to find the other's weak points, which a single agent tends to skip.

Useful when the risk is a one-sided answer: decisions, reviews, policies.

Use it when
Decisions with real trade-offs
Stress-testing a plan or an argument
Reducing one-sided answers
Skip it when
Questions with a single factual answer
Tight budgets: rounds × sides calls

Run it

Scripted model · no key needed · CriogenAI engine in your browser
Ready
rebutrebutQuestionForAgainstJudgeVerdict
The runmodel 0 · tools 0
  1. Press Run to watch the pattern work, step by step.
Try it with your own key optional: a real model makes the decisions; the tools stay simulated
  • Your key stays in this tab's memory: not saved, not in cookies or the address bar. Reload or close the tab and it is gone.
  • Your browser calls OpenAI directly. Nothing passes through criogenai.com, and nothing is logged.
  • Each run makes at most 16 short model calls, billed to your account. Tools, data and side effects are simulated.

This run is the TypeScript code below, running on the CriogenAI engine in your browser (WebAssembly). Without a key, a scripted model stands in for MODEL; with your key, your provider answers, called from this tab. The tools return made-up data.

Build it with CriogenAI

Built from primitives: Workflow: two agent nodes in a loop + judge (or patterns.review_and_critique)
sides = {
    "for": Agent(MODEL, instructions="Argue FOR. Answer the other side in two sentences."),
    "against": Agent(MODEL, instructions="Argue AGAINST. Answer the other side in two sentences."),
}
judge = Agent(MODEL, instructions="Judge the debate: the verdict and the deciding reason.")

def speaker(name: str, rounds: int = 2) -> Callable[[Dict[str, Any]], Any]:
    async def speak(state: Dict[str, Any]) -> Any:
        out = await sides[name].run("\n".join([state["input"], *state["log"]]))
        log = [*state["log"], f"{name}: {out.text}"]
        other = "against" if name == "for" else "for"
        return goto("judge" if len(log) >= 2 * rounds else other, {"log": log})

    return speak

async def decide(state: Dict[str, Any]) -> Dict[str, Any]:
    return {"output": (await judge.run("\n".join(state["log"]))).text}

wf = Workflow()
wf.add_node("for", speaker("for"))
wf.add_node("against", speaker("against"))
wf.add_node("judge", decide)
wf.goto("for", ["against", "judge"]).goto("against", ["for", "judge"])
wf.start("for").edge("judge", END)
result = await wf.compile().run({"input": "Should a 5-person startup build its own LLM gateway?", "log": []})
print(result.state["output"])
const sides: Record<string, Agent> = {
  for: new Agent({ model: MODEL, instructions: "Argue FOR. Answer the other side in two sentences." }),
  against: new Agent({ model: MODEL, instructions: "Argue AGAINST. Answer the other side in two sentences." }),
};
const judge = new Agent({ model: MODEL, instructions: "Judge the debate: the verdict and the deciding reason." });

const speaker = (name: string, rounds = 2) => async (state: State) => {
  const out = await sides[name].run([state.input, ...state.log].join("\n"));
  const log = [...state.log, `${name}: ${out.text}`];
  return goto(log.length >= 2 * rounds ? "judge" : name === "for" ? "against" : "for", { log });
};

const app = new Workflow()
  .node("for", speaker("for"))
  .node("against", speaker("against"))
  .node("judge", async (state) => ({ output: (await judge.run((state.log as string[]).join("\n"))).text }))
  .goto("for", ["against", "judge"])
  .goto("against", ["for", "judge"])
  .start("for")
  .edge("judge", END)
  .compile();
const result = await app.run({ input: task, log: [] });
console.log(result.state.output);