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#
Configure
Tracerwith JSONL + console output so each run emits machine-readable traces and lifecycle logs.Build role-specific delegates with
DirectLLMCallover one managedLlamaCppServerLLMClient.Execute
RalphLoopPattern.run(...)with dynamic roles, evaluator role id, and typedLoopConfig.Print a compact JSON payload including
trace_infofor 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"
}
}