Definition:
Loop Engineering is the discipline of designing, optimizing, and debugging the iterative feedback loops that an AI agent uses to reason, act, and learn from results. Instead of a single linear prompt → response flow (like a traditional LLM call), an agentic system runs in a loop: perceive → think → act → observe → think again → act again → repeat until the goal is reached.
The core loop that every agent system runs looks like this:
┌─────────────────────────────┐
│ 1. PERCEIVE │
│ (read tools, environment) │
└─────────────┬───────────────┘
▼
┌─────────────────────────────┐
│ 2. REASON │
│ (plan next action, │
│ evaluate context) │
└─────────────┬───────────────┘
▼
┌─────────────────────────────┐
│ 3. ACT │
│ (call a tool: terminal, │
│ read_file, web_search) │
└─────────────┬───────────────┘
▼
┌─────────────────────────────┐
│ 4. OBSERVE │
│ (tool returns output) │
└─────────────┬───────────────┘
│
┌───────┴───────┐
│ │
Goal met? Not yet?
│ │
▼ │
┌──────────┐ │
│ DONE │ │
└──────────┘ │
▲ │
└───────────────┘
Why the loop matters:
A non-agentic LLM call is one-shot: prompt → response. If the response is wrong, you have to manually correct it. An agentic loop turns this into a self-correcting process: the agent can run a command, see the output, realize it was wrong, and try a different approach — all without human intervention.
Key loop engineering patterns:
| Pattern | What It Does | Example |
|---|---|---|
| ReAct Loop | Reason → Act → Observe — interleave thinking with tool use | “I need to find the file. Let me search for it. I found it. Now let me read it. I see the bug. Now let me fix it.” |
| Reflection Loop | Generate output → Critique output → Refine | Write code → Check for bugs → Fix bugs → Recheck |
| Retry Loop | Try → Fail → Backoff → Retry | Call API → Timeout → Wait 1s → Retry (exponential backoff) |
| Supervision Loop | Act → Human approves? → Proceed or Abort | Deploy to prod → Ask user “Approve this change?” → Yes/No |
| Tree-of-Thought | Branch → Evaluate → Prune → Expand best branch | Generate 3 approaches → Score each → Explore the best one → Repeat |
| Planning Loop | Plan steps → Execute step 1 → Update plan → Execute step 2 | “Step 1: clone repo. Done. Step 2: install deps. Failed, try alternative.” |
Example:
A comparison of a non-agentic (single-shot) approach vs an agentic loop approach for the same task — finding and fixing a bug in a Python file.
import re
import json
import time
from dataclasses import dataclass, field
from typing import Optional, List
# ═══════════════════════════════════════════════
# Simulated Environment
# ═══════════════════════════════════════════════
# A simulated file with a bug
BUGGY_FILE = """
def calculate_discount(price: float, is_member: bool) -> float:
\"\"\"Apply member discount to price.\"\"\"
if is_member:
return price * 0.9 # 10% discount
else:
return price
def checkout(items: list[dict]) -> float:
\"\"\"Calculate total for all items in cart.\"\"\"
total = 0
for item in items:
total += item["price"]
member = items[0]["is_member"] # 🐛 BUG: assumes all items have same member status
return calculate_discount(total, member)
def test_checkout():
\"\"\"Test the checkout function.\"\"\"
cart = [
{"name": "shirt", "price": 25.0, "is_member": True},
{"name": "pants", "price": 50.0, "is_member": True},
]
result = checkout(cart)
expected = (25.0 + 50.0) * 0.9
print(f"Result: {result}, Expected: {expected}")
assert result == expected, f"Mismatch: {result} != {expected}"
print("✅ Test passed!")
test_checkout()
"""
# ═══════════════════════════════════════════════
# Approach 1: NON-AGENTIC (Single Shot)
# ═══════════════════════════════════════════════
class SingleShotAgent:
"""No loop. One prompt, one answer. Cannot self-correct."""
def run(self, task: str) -> dict:
# Simulating a single LLM call — no observation, no iteration
print(f"📝 Task: {task}")
print(f"🤖 Response: 'The bug is in line 12: 'member = items[0][\"is_member\"]'")
print(f" This assumes all items share the same member status.")
print(f" Fix: aggregate member status per item.'")
print(f"⚠️ But is the fix correct? The agent can't check.")
"Fix: Instead of taking member status from items[0], "
"check each item individually. If ANY item requires member "
"pricing logic, we need to apply discount per item or "
"determine member status differently."
)
if action == "run_test":
# Simulate running the test
result = (25.0 + 50.0) * 0.9
return f"Test output: Result: {result}, Expected: {result}. ✅ Test passed!"
if action == "verify_bug_exists":
# The test passes because both items have is_member=True!
# The bug only manifests with mixed membership.
return (
"⚠️ Test passed because both test items have is_member=True. "
"The bug is still there — it just doesn't show in this test case. "
"Need a better test with mixed member/non-member items to reproduce."
)
if action == "reproduce_bug":
# Create a test case that triggers the bug
return (
"Edge case test: cart with mixed membership\n"
"cart = [\n"
' {"name": "shirt", "price": 25.0, "is_member": True},\n'
' {"name": "pants", "price": 50.0, "is_member": False},\n'
"]\n"
"Expected: 25*0.9 + 50 = 72.5\n"
"Got: (25+50)*0.9 = 67.5\n"
"❌ Bug reproduced! Non-member item got a discount it shouldn't."
)
return "Unknown action"
def run(self, task: str) -> List[AgentAction]:
print(f"📝 Task: {task}\n")
print(f"🔄 AGENTIC LOOP — Step-by-Step\n")
print(f"{'='*60}")
loop = [
{
"thought": "First, I need to read the file to understand the bug.",
"action": "read_file",
"args": {},
},
{
"thought": "Let me search for suspicious patterns — 'items[0]' is often a bug.",
"action": "search",
"args": {"pattern": "items[0]"},
},
{
"thought": "Found 'items[0][\"is_member\"]'. This assumes all items share the same member status — that's the bug.",
"action": "analyze_bug",
"args": {},
},
{
"thought": "Let me verify by creating a test case that exposes the bug with mixed membership.",
"action": "reproduce_bug",
"args": {},
},
{
"thought": "Bug confirmed. The fix should calculate discount per-item based on each item's membership, not assume uniformity.",
"action": "propose_fix",
"args": {},
},
]
for i, step in enumerate(loop, 1):
start = time.time()
print(f"\n🔷 Step {i}/{len(loop)}")
print(f" 💭 {step['thought']}")
print(f" 🛠️ Action: {step['action']}({step['args']})")
result = self._simulate_tool_call(step["action"], step["args"])
elapsed = (time.time() - start) * 1000
print(f" 📥 Result:")
for line in result.split("\n")[:5]:
print(f" {line}")
if len(result.split("\n")) > 5:
print(f" ... ({len(result.split('\\n')) - 5} more lines)")
self.history.append(AgentAction(
step=i,
thought=step["thought"],
action=step["action"],
result=result,
duration_ms=round(elapsed, 1),
))
# Final reflection
reflection = (
f"\n✅ Loop complete after {len(loop)} steps.\n"
f" Diagnosis: Found bug at line 12 (items[0]['is_member'] assumes uniform membership)\n"
f" Reproduced: Created edge case that triggers the bug\n"
f" Verification: Non-member items incorrectly get discount\n"
f" Confidence: HIGH — bug is understood and reproducible\n"
)
print(reflection)
return self.history
# ═══════════════════════════════════════════════
# Run Comparison
# ═══════════════════════════════════════════════
print("📐 LOOP ENGINEERING — Agentic vs Non-Agentic Comparison")
print("=" * 70)
# Approach 1
print("\n❌ APPROACH 1: NON-AGENTIC (Single Shot)")
print("-" * 40)
single = SingleShotAgent()
single.run("Find the bug in checkout()")
# Approach 2
print("\n✅ APPROACH 2: AGENTIC LOOP (Perceive → Think → Act → Observe → Repeat)")
print("-" * 40)
looped = AgenticLoop()
actions = looped.run("Find the bug in checkout()")
# Summary comparison
print(f"\n{'='*70}")
print(f"📊 COMPARISON SUMMARY")
print(f"{'='*70}")
print(f"\n{'Dimension':<35} {'Single Shot':<18} {'Agentic Loop':<18}")
print(f"{'-'*70}")
print(f"{'Steps taken':<35} {'1 (one answer)':<18} {'5 (iterative)':<18}")
print(f"{'Self-corrects?':<35} {'❌ No':<18} {'✅ Yes — rethinks':<18}")
print(f"{'Can verify?':<35} {'❌ No':<18} {'✅ Yes — tests it':<18}")
print(f"{'Handles edge cases?':<35} {'❌ No':<18} {'✅ Yes — probes':<18}")
print(f"{'Finds root cause?':<35} {'❌ Surface only':<18} {'✅ Yes — deep':<18}")
print(f"{'Confidence':<35} {'Low (unverified)':<18} {'High (tested)':<18}")
print(f"{'Token cost':<35} {'Low':<18} {'Higher (but verified)':<18}")
print(f"\n{'='*70}")
print(f"🏁 KEY INSIGHT:")
print(f" The single-shot agent found the bug once (good!).")
print(f" But it couldn't verify its fix. The agentic loop went")
print(f" deeper — it discovered the test didn't reproduce the bug,")
print(f" created a proper edge case, confirmed the root cause,")
print(f" and built confidence through iteration.")
print(f" That's the power of loop engineering.")
📐 LOOP ENGINEERING — Agentic vs Non-Agentic Comparison
======================================================================
❌ APPROACH 1: NON-AGENTIC (Single Shot)
----------------------------------------
📝 Task: Find the bug in checkout()
🤖 Response: 'The bug is in line 12...'
⚠️ But is the fix correct? The agent can't check.
✅ APPROACH 2: AGENTIC LOOP (5 Steps)
----------------------------------------
🔷 Step 1: Read file to understand the bug
🔷 Step 2: Search for suspicious patterns — found items[0]
🔷 Step 3: Analyze — "items[0]['is_member'] assumes uniform membership"
🔷 Step 4: Reproduce — created edge case that triggers the bug
🔷 Step 5: Propose verified fix with high confidence
======================================================================
📊 COMPARISON SUMMARY
======================================================================
Dimension Single Shot Agentic Loop
----------------------------------------------------------------------
Self-corrects? ❌ No ✅ Yes — rethinks
Can verify? ❌ No ✅ Yes — tests it
Handles edge cases? ❌ No ✅ Yes — probes
Finds root cause? ❌ Surface only ✅ Yes — deep
Confidence Low (unverified) High (tested)