Ralph Loop#

Source: examples/patterns/ralph_loop.py

Introduction#

Ralph loops are role-programmed, not fixed two-role propose/critic cycles: each round executes an ordered role lineup, then a dedicated evaluator decides whether consensus quality is high enough. This example demonstrates a four-role configuration with synthesis selection and threshold stopping.

Note

This example’s checked-in local LlamaCppServerLLMClient config uses a Qwen3-4B GGUF model. On lower-RAM machines, swap in a smaller local model or start with Ollama Local Client.

Technical Implementation#

  1. Configure Tracer with JSONL + console output so each run emits machine-readable traces and lifecycle logs.

  2. Build role-specific delegates with DirectLLMCall over one managed LlamaCppServerLLMClient.

  3. Execute RalphLoopPattern.run(...) with dynamic roles, evaluator role id, and typed LoopConfig.

  4. Print a compact JSON payload including trace_info for deterministic tests and docs examples.

        flowchart LR
    A["Input prompt or scenario"] --> B["main(): runtime wiring"]
    B --> C["RalphLoopPattern.run(...)"]
    C --> D["role batch executes proposer/critic/synthesizer/evaluator each round"]
    C --> E["evaluator score compared to consensus threshold"]
    D --> F["ExecutionResult/payload"]
    E --> F
    F --> G["Printed JSON output"]
    
  1from __future__ import annotations
  2
  3import json
  4from pathlib import Path
  5
  6from design_research_agents import DirectLLMCall, LlamaCppServerLLMClient, Tracer
  7from design_research_agents.patterns import RalphLoopPattern
  8
  9# This checked-in local config uses a Qwen3-4B GGUF model to exercise a richer
 10# multi-step path. On lower-RAM machines, swap in a smaller local model or
 11# start with the lighter Ollama local client example first.
 12_EXAMPLE_LLAMA_CLIENT_KWARGS = {
 13    "model": "Qwen_Qwen3-4B-Instruct-2507-Q4_K_M.gguf",
 14    "hf_model_repo_id": "bartowski/Qwen_Qwen3-4B-Instruct-2507-GGUF",
 15    "api_model": "qwen3-4b-instruct-2507-q4km",
 16    "context_window": 8192,
 17    "startup_timeout_seconds": 240.0,
 18    "request_timeout_seconds": 240.0,
 19}
 20
 21
 22def main() -> None:
 23    """Run one Ralph loop workflow and print JSON summary."""
 24    request_id = "example-pattern-ralph-loop-design-001"
 25    tracer = Tracer(
 26        enabled=True,
 27        trace_dir=Path("artifacts/examples/traces"),
 28        enable_jsonl=True,
 29        enable_console=True,
 30    )
 31    with LlamaCppServerLLMClient(**_EXAMPLE_LLAMA_CLIENT_KWARGS) as llm_client:
 32        proposer = DirectLLMCall(
 33            llm_client=llm_client,
 34            system_prompt=("You are a design proposer. Return concise JSON with proposal options and intended change."),
 35            tracer=tracer,
 36        )
 37        critic = DirectLLMCall(
 38            llm_client=llm_client,
 39            system_prompt="You are a design critic. Return concise JSON with risks and revision advice.",
 40            tracer=tracer,
 41        )
 42        synthesizer = DirectLLMCall(
 43            llm_client=llm_client,
 44            system_prompt=(
 45                "You are a synthesis role. Merge proposal + critique into one implementation-ready JSON summary."
 46            ),
 47            tracer=tracer,
 48        )
 49        evaluator = DirectLLMCall(
 50            llm_client=llm_client,
 51            system_prompt=("You are the evaluator. Return JSON with numeric score in [0,1] and brief rationale."),
 52            tracer=tracer,
 53        )
 54
 55        pattern = RalphLoopPattern(
 56            roles=(
 57                RalphLoopPattern.RoleSpec(
 58                    role_id="proposer",
 59                    delegate=proposer,
 60                    prompt_template=(
 61                        "Task: {task}\nIteration: {iteration}\nCurrent selected output:"
 62                        " {selected_output_json}\nReturn JSON for the next proposal."
 63                    ),
 64                ),
 65                RalphLoopPattern.RoleSpec(
 66                    role_id="critic",
 67                    delegate=critic,
 68                    prompt_template=(
 69                        "Task: {task}\nIteration: {iteration}\nPrior role outputs:"
 70                        " {prior_role_outputs_json}\nReturn JSON critique for the proposer."
 71                    ),
 72                ),
 73                RalphLoopPattern.RoleSpec(
 74                    role_id="synthesizer",
 75                    delegate=synthesizer,
 76                    prompt_template=(
 77                        "Task: {task}\nIteration: {iteration}\nPrior role outputs:"
 78                        " {prior_role_outputs_json}\nReturn JSON synthesis ready for evaluation."
 79                    ),
 80                ),
 81                RalphLoopPattern.RoleSpec(
 82                    role_id="evaluator",
 83                    delegate=evaluator,
 84                    prompt_template=(
 85                        "Task: {task}\nIteration: {iteration}\nCandidate synthesis:"
 86                        " {selected_output_json}\nRole outputs: {prior_role_outputs_json}\n"
 87                        "Return JSON with score in [0,1]."
 88                    ),
 89                ),
 90            ),
 91            evaluator_role_id="evaluator",
 92            loop_config=RalphLoopPattern.LoopConfig(
 93                max_iterations=3,
 94                consensus_threshold=0.8,
 95                selection_strategy="best_score",
 96            ),
 97            tracer=tracer,
 98        )
 99
100        result = pattern.run(
101            "Refine a field-serviceable edge-device enclosure concept.",
102            request_id=request_id,
103        )
104    print(json.dumps(result.summary(), ensure_ascii=True, indent=2, sort_keys=True))
105
106
107if __name__ == "__main__":
108    main()

Expected Results#

Run Command

PYTHONPATH=src python3 examples/patterns/ralph_loop.py

Example output shape (values vary by run):

{
  "success": true,
  "final_output": "<example-specific payload>",
  "terminated_reason": "<string-or-null>",
  "error": null,
  "trace": {
    "request_id": "<request-id>",
    "trace_dir": "artifacts/examples/traces",
    "trace_path": "artifacts/examples/traces/run_<timestamp>_<request_id>.jsonl"
  }
}

References#