Agentic Loop (Ralph Loop Pattern)
Implement the autonomous task execution pattern where an AI agent reads a plan from disk, executes one task per iteration in a fresh context, and uses validation as backpressure.
How It Worksโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ IMPLEMENTATION_PLAN.md (disk) โ
โ โฌ Task 1: Scaffold structure โ
โ โฌ Task 2: Create agent โ
โ โฌ Task 3: Write tests โ
โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโผโโโโโโโ
โ Iteration 1 โ โ Fresh context
โ Execute T1 โ
โ Mark โ
โ
โ Run tests โ โ Backpressure
โโโโโโโโฌโโโโโโโ
โ
โโโโโโโโผโโโโโโโ
โ Iteration 2 โ โ Fresh context
โ Execute T2 โ
โ ... โ
โโโโโโโโโโโโโโโ
Step 1: Create the Implementation Planโ
The plan file is the shared state between iterations:
spec/implementation-plan.md
# Implementation Plan
## Tasks
- [ ] Task 1: Create the project structure with fai-manifest.json
- [ ] Task 2: Build the RAG ingestion pipeline
- [ ] Task 3: Create the retrieval API endpoint
- [ ] Task 4: Add evaluation pipeline
- [ ] Task 5: Write integration tests
- [ ] Task 6: Deploy to Azure Container Apps
## Context
- Play: 01-enterprise-rag
- Stack: Python + FastAPI + Azure AI Search
## Completion Criteria
- All tasks checked โ
- `npm run validate:primitives` exits 0
- All tests pass
Step 2: Create the Loop Runnerโ
scripts/agentic-loop.js
const fs = require('fs');
const { execSync } = require('child_process');
const PLAN_FILE = process.argv[2] || 'spec/implementation-plan.md';
const MAX_ITERATIONS = 20;
function getNextTask(plan) {
const lines = plan.split('\n');
for (const line of lines) {
if (line.match(/^- \[ \] /)) {
return line.replace('- [ ] ', '').trim();
}
}
return null;
}
function markTaskDone(task) {
const plan = fs.readFileSync(PLAN_FILE, 'utf8');
fs.writeFileSync(PLAN_FILE, plan.replace(`- [ ] ${task}`, `- [x] ${task}`));
}
function runValidation() {
try {
execSync('node scripts/validate-primitives.js', { stdio: 'inherit' });
return true;
} catch {
return false;
}
}
for (let i = 0; i < MAX_ITERATIONS; i++) {
const plan = fs.readFileSync(PLAN_FILE, 'utf8');
const task = getNextTask(plan);
if (!task) {
console.log('โ
All tasks complete!');
break;
}
console.log(`\n๐ Iteration ${i + 1}: ${task}`);
// Agent executes the task with fresh context
markTaskDone(task);
if (!runValidation()) {
console.log('โ Validation failed โ stopping loop');
break;
}
}
Step 3: Multi-Agent Routingโ
Assign different agents to different task types:
| Task Pattern | Agent | Expertise |
|---|---|---|
| "Create structure" | fai-architect | Architecture, scaffolding |
| "Build pipeline" | fai-play-01-builder | Play-specific implementation |
| "Write tests" | fai-test-generator | Test generation |
| "Deploy" | fai-devops-expert | Infrastructure, deployment |
| "Evaluate" | fai-play-01-reviewer | Quality review |
Step 4: Add Evaluation Backpressureโ
function runEvaluation(playId) {
try {
const result = execSync(
`node engine/index.js solution-plays/${playId}/fai-manifest.json --eval`,
{ encoding: 'utf8' }
);
return result.includes('passed');
} catch {
return false;
}
}
Why Fresh Context Mattersโ
- No hallucination accumulation โ previous mistakes don't pollute future tasks
- Token budget reset โ each task gets the full context window
- Parallel potential โ independent tasks could run simultaneously
- Reproducibility โ same plan + same task = same result
Disk-Based State Patternsโ
| Pattern | File | Purpose |
|---|---|---|
| Task tracking | spec/implementation-plan.md | - [ ] โ - [x] |
| Iteration log | spec/iteration-log.jsonl | Append-only log |
| Artifacts | spec/artifacts/ | Generated files |
| Error log | spec/errors.md | Failed tasks |
Best Practicesโ
- One task per iteration โ don't batch multiple tasks
- Plan on disk, not in memory โ survives crashes
- Validation as backpressure โ never proceed if tests fail
- Log everything โ append to
iteration-log.jsonl - Set MAX_ITERATIONS โ prevent infinite loops
- Fresh context per iteration โ avoid context window pollution
See Alsoโ
- Evaluate a Play โ evaluation as backpressure
- Agents Reference โ multi-agent routing