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 passStep 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
Last updated on