Skip to content

Commit d8fc473

Browse files
committed
Add RALPH-loop recipe to Copilot SDK cookbook
Add iterative RALPH-loop (Read, Act, Log, Persist, Halt) pattern implementations for all four supported languages: - C#/.NET: ralph-loop.cs with documentation - Node.js/TypeScript: ralph-loop.ts with documentation - Python: ralph_loop.py with documentation (async API) - Go: ralph-loop.go with documentation Each recipe demonstrates: - Self-referential iteration where AI reviews its own output - Completion promise detection to halt the loop - Max iteration safety limits - File persistence between iterations Verified against real Copilot SDK APIs: - Python: fully verified end-to-end with github-copilot-sdk - Node.js: fully verified end-to-end with @github/copilot-sdk - C#: compiles and runs successfully with GitHub.Copilot.SDK - Go: compiles against github.com/github/copilot-sdk/go v0.1.23
1 parent 4555fee commit d8fc473

File tree

9 files changed

+1403
-1
lines changed

9 files changed

+1403
-1
lines changed

cookbook/copilot-sdk/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
66

77
### .NET (C#)
88

9+
- [RALPH-loop](dotnet/ralph-loop.md): Implement iterative self-referential AI loops for task completion with automatic retries.
910
- [Error Handling](dotnet/error-handling.md): Handle errors gracefully including connection failures, timeouts, and cleanup.
1011
- [Multiple Sessions](dotnet/multiple-sessions.md): Manage multiple independent conversations simultaneously.
1112
- [Managing Local Files](dotnet/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
@@ -14,6 +15,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
1415

1516
### Node.js / TypeScript
1617

18+
- [RALPH-loop](nodejs/ralph-loop.md): Implement iterative self-referential AI loops for task completion with automatic retries.
1719
- [Error Handling](nodejs/error-handling.md): Handle errors gracefully including connection failures, timeouts, and cleanup.
1820
- [Multiple Sessions](nodejs/multiple-sessions.md): Manage multiple independent conversations simultaneously.
1921
- [Managing Local Files](nodejs/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
@@ -22,6 +24,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
2224

2325
### Python
2426

27+
- [RALPH-loop](python/ralph-loop.md): Implement iterative self-referential AI loops for task completion with automatic retries.
2528
- [Error Handling](python/error-handling.md): Handle errors gracefully including connection failures, timeouts, and cleanup.
2629
- [Multiple Sessions](python/multiple-sessions.md): Manage multiple independent conversations simultaneously.
2730
- [Managing Local Files](python/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
@@ -30,6 +33,7 @@ This cookbook collects small, focused recipes showing how to accomplish common t
3033

3134
### Go
3235

36+
- [RALPH-loop](go/ralph-loop.md): Implement iterative self-referential AI loops for task completion with automatic retries.
3337
- [Error Handling](go/error-handling.md): Handle errors gracefully including connection failures, timeouts, and cleanup.
3438
- [Multiple Sessions](go/multiple-sessions.md): Manage multiple independent conversations simultaneously.
3539
- [Managing Local Files](go/managing-local-files.md): Organize files by metadata using AI-powered grouping strategies.
@@ -83,4 +87,4 @@ go run <filename>.go
8387

8488
## Status
8589

86-
Cookbook structure is complete with 4 recipes across all 4 supported languages. Each recipe includes both markdown documentation and runnable examples.
90+
Cookbook structure is complete with 5 recipes across all 4 supported languages. Each recipe includes both markdown documentation and runnable examples. The RALPH-loop recipe demonstrates iterative self-referential AI loops for autonomous task completion.
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
# RALPH-loop: Iterative Self-Referential AI Loops
2+
3+
Implement self-referential feedback loops where an AI agent iteratively improves work by reading its own previous output.
4+
5+
> **Runnable example:** [recipe/ralph-loop.cs](recipe/ralph-loop.cs)
6+
>
7+
> ```bash
8+
> cd dotnet/recipe
9+
> dotnet run ralph-loop.cs
10+
> ```
11+
12+
## What is RALPH-loop?
13+
14+
RALPH-loop is a development methodology for iterative AI-powered task completion. Named after the Ralph Wiggum technique, it embodies the philosophy of persistent iteration:
15+
16+
- **One prompt, multiple iterations**: The same prompt is processed repeatedly
17+
- **Self-referential feedback**: The AI reads its own previous work (file changes, git history)
18+
- **Completion detection**: Loop exits when a completion promise is detected in output
19+
- **Safety limits**: Always include a maximum iteration count to prevent infinite loops
20+
21+
## Example Scenario
22+
23+
You need to iteratively improve code until all tests pass. Instead of asking Claude to "write perfect code," you use RALPH-loop to:
24+
25+
1. Send the initial prompt with clear success criteria
26+
2. Claude writes code and tests
27+
3. Claude runs tests and sees failures
28+
4. Loop automatically re-sends the prompt
29+
5. Claude reads test output and previous code, fixes issues
30+
6. Repeat until all tests pass and completion promise is output
31+
32+
## Basic Implementation
33+
34+
```csharp
35+
using GitHub.Copilot.SDK;
36+
37+
public class RalphLoop
38+
{
39+
private readonly CopilotClient _client;
40+
private int _iteration = 0;
41+
private readonly int _maxIterations;
42+
private readonly string _completionPromise;
43+
private string? _lastResponse;
44+
45+
public RalphLoop(int maxIterations = 10, string completionPromise = "COMPLETE")
46+
{
47+
_client = new CopilotClient();
48+
_maxIterations = maxIterations;
49+
_completionPromise = completionPromise;
50+
}
51+
52+
public async Task<string> RunAsync(string prompt)
53+
{
54+
await _client.StartAsync();
55+
var session = await _client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
56+
57+
try
58+
{
59+
while (_iteration < _maxIterations)
60+
{
61+
_iteration++;
62+
Console.WriteLine($"\n--- Iteration {_iteration} ---");
63+
64+
var done = new TaskCompletionSource<string>();
65+
session.On(evt =>
66+
{
67+
if (evt is AssistantMessageEvent msg)
68+
{
69+
_lastResponse = msg.Data.Content;
70+
done.SetResult(msg.Data.Content);
71+
}
72+
});
73+
74+
// Send prompt (on first iteration) or continuation
75+
var messagePrompt = _iteration == 1
76+
? prompt
77+
: $"{prompt}\n\nPrevious attempt:\n{_lastResponse}\n\nContinue iterating...";
78+
79+
await session.SendAsync(new MessageOptions { Prompt = messagePrompt });
80+
var response = await done.Task;
81+
82+
// Check for completion promise
83+
if (response.Contains(_completionPromise))
84+
{
85+
Console.WriteLine($"✓ Completion promise detected: {_completionPromise}");
86+
return response;
87+
}
88+
89+
Console.WriteLine($"Iteration {_iteration} complete. Continuing...");
90+
}
91+
92+
throw new InvalidOperationException(
93+
$"Max iterations ({_maxIterations}) reached without completion promise");
94+
}
95+
finally
96+
{
97+
await session.DisposeAsync();
98+
await _client.StopAsync();
99+
}
100+
}
101+
}
102+
103+
// Usage
104+
var loop = new RalphLoop(maxIterations: 5, completionPromise: "DONE");
105+
var result = await loop.RunAsync("Your task here");
106+
Console.WriteLine(result);
107+
```
108+
109+
## With File Persistence
110+
111+
For tasks involving code generation, persist state to files so the AI can see changes:
112+
113+
```csharp
114+
public class PersistentRalphLoop
115+
{
116+
private readonly string _workDir;
117+
private readonly CopilotClient _client;
118+
private int _iteration = 0;
119+
120+
public PersistentRalphLoop(string workDir, int maxIterations = 10)
121+
{
122+
_workDir = workDir;
123+
Directory.CreateDirectory(_workDir);
124+
_client = new CopilotClient();
125+
}
126+
127+
public async Task<string> RunAsync(string prompt)
128+
{
129+
await _client.StartAsync();
130+
var session = await _client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
131+
132+
try
133+
{
134+
// Store initial prompt
135+
var promptFile = Path.Combine(_workDir, "prompt.md");
136+
await File.WriteAllTextAsync(promptFile, prompt);
137+
138+
while (_iteration < 10)
139+
{
140+
_iteration++;
141+
Console.WriteLine($"\n--- Iteration {_iteration} ---");
142+
143+
// Build context including previous work
144+
var contextBuilder = new StringBuilder(prompt);
145+
var previousOutput = Path.Combine(_workDir, $"output-{_iteration - 1}.txt");
146+
if (File.Exists(previousOutput))
147+
{
148+
contextBuilder.AppendLine($"\nPrevious iteration output:\n{await File.ReadAllTextAsync(previousOutput)}");
149+
}
150+
151+
var done = new TaskCompletionSource<string>();
152+
string response = "";
153+
session.On(evt =>
154+
{
155+
if (evt is AssistantMessageEvent msg)
156+
{
157+
response = msg.Data.Content;
158+
done.SetResult(msg.Data.Content);
159+
}
160+
});
161+
162+
await session.SendAsync(new MessageOptions { Prompt = contextBuilder.ToString() });
163+
await done.Task;
164+
165+
// Persist output
166+
await File.WriteAllTextAsync(
167+
Path.Combine(_workDir, $"output-{_iteration}.txt"),
168+
response);
169+
170+
if (response.Contains("COMPLETE"))
171+
{
172+
return response;
173+
}
174+
}
175+
176+
throw new InvalidOperationException("Max iterations reached");
177+
}
178+
finally
179+
{
180+
await session.DisposeAsync();
181+
await _client.StopAsync();
182+
}
183+
}
184+
}
185+
```
186+
187+
## Best Practices
188+
189+
1. **Write clear completion criteria**: Include exactly what "done" looks like
190+
2. **Use output markers**: Include `<promise>COMPLETE</promise>` or similar in completion condition
191+
3. **Always set max iterations**: Prevents infinite loops on impossible tasks
192+
4. **Persist state**: Save files so AI can see what changed between iterations
193+
5. **Include context**: Feed previous iteration output back as context
194+
6. **Monitor progress**: Log each iteration to track what's happening
195+
196+
## Example: Iterative Code Generation
197+
198+
```csharp
199+
var prompt = @"Write a function that:
200+
1. Parses CSV data
201+
2. Validates required fields
202+
3. Returns parsed records or error
203+
4. Has unit tests
204+
5. Output <promise>COMPLETE</promise> when done";
205+
206+
var loop = new RalphLoop(maxIterations: 10, completionPromise: "COMPLETE");
207+
var result = await loop.RunAsync(prompt);
208+
```
209+
210+
## Handling Failures
211+
212+
```csharp
213+
try
214+
{
215+
var result = await loop.RunAsync(prompt);
216+
Console.WriteLine("Task completed successfully!");
217+
}
218+
catch (InvalidOperationException ex) when (ex.Message.Contains("Max iterations"))
219+
{
220+
Console.WriteLine("Task did not complete within iteration limit.");
221+
Console.WriteLine($"Last response: {loop.LastResponse}");
222+
// Document what was attempted and suggest alternatives
223+
}
224+
```
225+
226+
## When to Use RALPH-loop
227+
228+
**Good for:**
229+
- Code generation with automatic verification (tests, linters)
230+
- Tasks with clear success criteria
231+
- Iterative refinement where each attempt learns from previous failures
232+
- Unattended long-running improvements
233+
234+
**Not good for:**
235+
- Tasks requiring human judgment or design input
236+
- One-shot operations
237+
- Tasks with vague success criteria
238+
- Real-time interactive debugging

0 commit comments

Comments
 (0)