Skip to main content

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 PatternAgentExpertise
"Create structure"fai-architectArchitecture, scaffolding
"Build pipeline"fai-play-01-builderPlay-specific implementation
"Write tests"fai-test-generatorTest generation
"Deploy"fai-devops-expertInfrastructure, deployment
"Evaluate"fai-play-01-reviewerQuality 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โ€‹

  1. No hallucination accumulation โ€” previous mistakes don't pollute future tasks
  2. Token budget reset โ€” each task gets the full context window
  3. Parallel potential โ€” independent tasks could run simultaneously
  4. Reproducibility โ€” same plan + same task = same result

Disk-Based State Patternsโ€‹

PatternFilePurpose
Task trackingspec/implementation-plan.md- [ ] โ†’ - [x]
Iteration logspec/iteration-log.jsonlAppend-only log
Artifactsspec/artifacts/Generated files
Error logspec/errors.mdFailed tasks

Best Practicesโ€‹

  1. One task per iteration โ€” don't batch multiple tasks
  2. Plan on disk, not in memory โ€” survives crashes
  3. Validation as backpressure โ€” never proceed if tests fail
  4. Log everything โ€” append to iteration-log.jsonl
  5. Set MAX_ITERATIONS โ€” prevent infinite loops
  6. Fresh context per iteration โ€” avoid context window pollution

See Alsoโ€‹