Patterns

Ralph Wiggum

A self-refining agent pattern that runs ReAct repeatedly with fresh context until a verification function passes.

Overview

Ralph Wiggum is a self-refining agent pattern. It runs a full ReAct loop inside a spawn (fresh context each time), checks the output with a verify function, and if verification fails, feeds the feedback into the next iteration. This continues until verification passes or the iteration limit is reached.

The name is a playful reference -- like Ralph Wiggum, it keeps trying until it gets it right.

Signature

declare function ralphWiggum(opts: {
  model: string;
  instructions: string;
  tools: Tool[];
  verify: VerifyFn;
  maxIterations?: number;
  innerMaxSteps?: number;
}): StepLoop<ContextMemory, string, string>;
ParameterTypeDefaultPurpose
modelstring--Model identifier
instructionsstring--System prompt
toolsTool[]--Available tools
verifyVerifyFn--Function that checks if the output is acceptable
maxIterationsnumber50Maximum outer loop iterations
innerMaxStepsnumber20Maximum steps per inner ReAct loop

VerifyFn

type VerifyFn = (output: unknown) => Promise<{
  pass: boolean;
  feedback?: string;
}>;

The feedback string is prepended to the next iteration's input, giving the agent specific guidance on what to fix.

Example

import { ralphWiggum } from '@noetic/core';
import type { Tool } from '@noetic/core';

declare const codeExecutionTool: Tool;

const agent = ralphWiggum({
  model: 'gpt-4o',
  instructions: 'Write a Python function that sorts a list using merge sort.',
  tools: [codeExecutionTool],
  verify: async (output) => {
    const code = String(output);
    const hasFunction = code.includes('def merge_sort');
    const hasRecursion = code.includes('merge_sort(');
    return {
      pass: hasFunction && hasRecursion,
      feedback: !hasFunction
        ? 'Output must define a merge_sort function.'
        : 'Function must use recursion.',
    };
  },
  maxIterations: 5,
  innerMaxSteps: 10,
});

How It Works

  1. The outer loop spawns with empty context.
  2. The inner ReAct agent runs to completion (up to innerMaxSteps).
  3. The output is checked with until.verified(verify).
  4. If verification fails, prepareNext injects the feedback: "Previous attempt feedback: {feedback}\nContinue working.".
  5. A new spawn starts with fresh context but the feedback from the previous attempt.
  6. This repeats until verify returns { pass: true } or maxIterations is reached.
import { any, react, spawn, until } from '@noetic/core';
import type { Tool, VerifyFn } from '@noetic/core';

function ralphWiggumImpl(opts: {
  model: string;
  instructions: string;
  tools: Tool[];
  verify: VerifyFn;
  maxIterations?: number;
  innerMaxSteps?: number;
}) {
  const inner = react({
    model: opts.model,
    instructions: opts.instructions,
    tools: opts.tools,
    maxSteps: opts.innerMaxSteps ?? 20,
  });

  return {
    kind: 'loop' as const,
    id: 'ralph-wiggum-loop',
    steps: [
      spawn({
        id: 'ralph-iteration',
        child: inner,
      }),
    ],
    until: any(until.verified(opts.verify), until.maxSteps(opts.maxIterations ?? 50)),
    prepareNext: (_output: string, verdict: { feedback?: string }) => {
      if (verdict.feedback) {
        return `Previous attempt feedback: ${verdict.feedback}\nContinue working.`;
      }
      return 'Continue working on the task.';
    },
  };
}

When to Use

  • Code generation where output must pass tests
  • Content that must meet specific quality criteria
  • Any task where you can programmatically verify correctness
  • Tasks where the LLM benefits from seeing what went wrong

Tips

  • Keep verify fast -- it runs on every iteration.
  • Use specific feedback strings. Vague feedback like "try again" wastes iterations.
  • Set innerMaxSteps high enough for the inner agent to finish, but low enough to avoid runaway costs.
  • Fresh context on each iteration prevents the agent from getting stuck in a rut.

On this page