Understanding the Generator-Critic Split in ADHD: Architecture and Implementation

The generator-critic split in ADHD separates creative content generation from analytical evaluation by issuing distinct LLM calls for each role, enabling parallel divergence and focused convergence.

The UditAkhourii/adhd repository implements Asynchronous Divergent Hierarchical Decoding (ADHD), a framework that treats large language model inference as a two-stage pipeline. At its core lies the generator-critic split, an architectural pattern that assigns distinct responsibilities to different LLM invocations. This design ensures that creative exploration and critical judgment remain isolated processes, maximizing the quality of both phases.

What Is the Generator-Critic Split?

The architecture divides the LLM workload into two specialized personas that operate sequentially but never within the same call context.

The Generator Role (Divergent Phase)

The generator produces multiple independent continuations of a given prompt. In src/engine.ts, the system explicitly configures the model for this role by injecting a prompt that states: "You are in DIVERGENT mode. You are a generator, not a critic" L61-L62. This instruction primes the model for maximum creativity and variability.

The Critic Role (Convergent Phase)

The critic evaluates the generator's outputs and selects the most promising candidate. During convergence, the engine swaps the system prompt to emphasize evaluation, reasoning, and selection criteria L328. This role requires analytical rigor rather than creative exploration.

How the Split Is Implemented in the Code

src/llm.ts provides the stateless execution layer. The callLLM function wraps the Claude Agent SDK to create a fresh session for every invocation. As documented in the file header, "Each divergent branch is its own query() call so they run in true parallel — this is the 'ADHD' fan-out" L5-L7. This statelessness guarantees that generator branches remain unaware of each other's outputs.

src/engine.ts orchestrates the workflow transitions. It manages the divergent phase by spawning multiple generator calls with distinct random seeds or framing prompts, then initiates the convergent phase by invoking the critic with the aggregated results. The engine can also override the default model for the critic separately from the generator L328.

src/frames.ts enforces diversity across generator calls. The file contains logic that "pushes the generator into corners it wouldn't naturally go" L1-L2, ensuring that the parallel branches explore disjoint regions of the solution space rather than converging prematurely on similar answers.

src/cli.ts exposes user-facing configuration. The --model flag sets the base model used for both roles L99, but the underlying engine still routes generator and critic tasks through separate callLLM invocations, preserving the architectural split even when the model identifier is identical.

Why ADHD Uses Separate LLM Calls

  • True Parallelism: Each generator branch executes as an independent query() invocation, allowing the SDK to run calls concurrently up to provider limits. This maximizes throughput and prevents the "mixing kills idea quality" problem noted in the source code.
  • Stateless Generation: By resetting the session for every branch, the generator cannot see sibling outputs, preserving independence and preventing cross-contamination of ideas.
  • Focused Evaluation: The critic receives a dedicated system prompt emphasizing analytical reasoning. Isolating this call prevents the model from conflating creative constraints with evaluative criteria.
  • Modular Control: Users can theoretically deploy different models for each role (e.g., a creative model for generation and a reasoning-optimized model for criticism) because the LLMOptions interface supports distinct configurations per call.

Practical Code Examples

The following snippets demonstrate how to invoke the generator and critic roles separately using the ADHD TypeScript API.

// 1️⃣  Create a divergent (generator) request
import { callLLM, LLMOptions } from "./src/llm";

const genOpts: LLMOptions = {
  systemPrompt: `You are in DIVERGENT mode. You are a generator, not a critic.`,
  userPrompt: `Write three wildly different opening paragraphs for a sci-fi short story.`,
};

const genResult = await callLLM(genOpts);   // ← separate LLM call
console.log("Generator output:", genResult);
// 2️⃣  Create a convergent (critic) request
const critOpts: LLMOptions = {
  systemPrompt: `You are in CONVERGENT mode. You are a critic, evaluating the proposals above.`,
  userPrompt: `Given the three paragraphs, choose the one that best invites curiosity and explain why.`,
};

const critResult = await callLLM(critOpts); // ← separate LLM call
console.log("Critic decision:", critResult);

Running the CLI (npx adhd --model claude-3-opus --prompt "Design a database schema") triggers this flow automatically; the engine spawns parallel generator calls, collects their outputs, then invokes the critic call to select the winner.

Summary

  • The generator-critic split divides ADHD into divergent (creative) and convergent (evaluative) phases to optimize for different cognitive tasks.
  • src/llm.ts provides stateless, parallel-ready LLM calls via the callLLM function, ensuring each branch operates in isolation.
  • src/engine.ts orchestrates mode switching through system prompt injection and manages the handoff between generation and evaluation.
  • Separate LLM calls prevent idea cross-contamination, enable true parallelism, and allow flexible model selection for each role.

Frequently Asked Questions

What is the difference between the generator and critic in ADHD?

The generator creates multiple diverse outputs in parallel during the divergent phase, while the critic evaluates these outputs during the convergent phase to select the highest quality result. This separation allows each role to optimize for different objectives—creativity versus analysis—without interference.

Why doesn't ADHD use a single LLM call for both generation and evaluation?

A single call would force the model to mix creative and analytical contexts, degrading the quality of both tasks. Separate calls maintain stateless independence for generators (preventing cross-contamination of ideas) and allow the critic to adopt a purely evaluative stance without creative biases or constraints bleeding into the judgment process.

How does the engine.ts file manage the transition between generator and critic modes?

The src/engine.ts file switches system prompts based on the current phase. It injects a generator-specific prompt during divergence L61-L62 and swaps to a critic-oriented prompt during convergence L328, effectively reconfiguring the LLM's role without changing the underlying API client configuration.

Can I use different models for the generator and critic in ADHD?

While the CLI's --model flag sets a default model used for both roles L99, the architecture supports modular control through the LLMOptions interface. This allows the engine to specify different model parameters or even different model identifiers for each call, enabling power users to deploy specialized models optimized for generation versus evaluation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →