Home Blog Series C C05
Series C: Prompt / Context / Harness / Loop Engineering · C05 AI & Agentic Systems 10 November 2025 11 min read

Loop Engineering Lanjutan — Reflection, Self-Correction & Multi-Agent

Implementasi penuh tiga pola loop engineering: reflection agent, self-correcting structured output, dan critique-revise dengan dua LLM berbeda — dengan kode production-ready.

RM
Ryan Muliadi
Digital Systems Architect · Ryzen Digital Systems

Di Series A Episode 04, saya memperkenalkan tiga pola loop engineering: reflection, self-correction, dan critique-revise. Artikel ini adalah implementasi penuhnya — dengan kode yang bisa langsung dipakai dan kondisi kapan masing-masing cocok.

Recap: Kenapa Loop Engineering

Loop Engineering dalam Konteks
Prompt engineering = instruksi yang benar. Context engineering = informasi yang tepat. Loop engineering = iterasi yang memperbaiki output sampai memenuhi kualitas yang diinginkan. Tiga layer berbeda, bukan substitusi satu sama lain.

Reflection Loop — Implementasi Penuh

Reflection loop adalah pola paling sederhana: LLM mengevaluasi output-nya sendiri, identifikasi kekurangan, dan revisi. Tiga langkah dalam satu agen:

Python · Production Reflection Agent
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, SystemMessage
from dataclasses import dataclass

@dataclass
class ReflectionResult:
    final_output: str
    iterations: int
    reflection_history: list
    stopped_early: bool

def reflection_agent(
    llm: ChatAnthropic,
    task: str,
    criteria: str,         # kriteria kualitas yang harus dipenuhi
    max_reflections: int = 3
) -> ReflectionResult:

    history = []
    draft = llm.invoke([
        SystemMessage("Selesaikan tugas yang diberikan dengan baik dan detail."),
        HumanMessage(task)
    ]).content

    for i in range(max_reflections):
        # Critique phase
        critique = llm.invoke([
            SystemMessage("""Anda adalah evaluator kritis.
Evaluasi draft dan cek apakah memenuhi kriteria.
Jika sudah memenuhi semua kriteria, balas tepat: APPROVED
Jika belum, sebutkan secara spesifik apa yang kurang."""),
            HumanMessage(f"""Tugas asli: {task}

Kriteria kualitas:
{criteria}

Draft yang perlu dievaluasi:
{draft}"")
        ]).content

        history.append({"iteration": i+1, "draft": draft, "critique": critique})

        if "APPROVED" in critique:
            return ReflectionResult(
                final_output=draft, iterations=i+1,
                reflection_history=history, stopped_early=True
            )

        # Revise phase
        draft = llm.invoke([
            SystemMessage("Perbaiki draft berdasarkan feedback yang diberikan."),
            HumanMessage(f"""Draft sebelumnya:
{draft}

Feedback yang perlu diperbaiki:
{critique}

Hasilkan versi yang lebih baik.""")
        ]).content

    return ReflectionResult(
        final_output=draft, iterations=max_reflections,
        reflection_history=history, stopped_early=False
    )

# Pemakaian
result = reflection_agent(
    llm=ChatAnthropic(model="claude-3-5-sonnet-20241022"),
    task="Buat email penolakan proposal yang tetap menjaga hubungan baik",
    criteria="""
- Tone harus empati dan menghargai effort mereka
- Alasan penolakan harus jelas tapi tidak menyinggung
- Ada opening untuk kolaborasi di masa depan
- Panjang 150-200 kata
- Tidak ada kalimat klise seperti "terima kasih sudah menghubungi kami"
"""
)

Self-Correction Loop — dengan Structured Validation

Berbeda dari reflection (evaluasi kualitatif), self-correction digunakan ketika ada constraint yang bisa diverifikasi secara programatik. LLM tidak perlu "diyakinkan" — cukup tunjukkan error yang konkret:

Python · Self-Correction dengan Schema Validation
from pydantic import BaseModel, ValidationError
from typing import Optional, Type, TypeVar

T = TypeVar('T', bound=BaseModel)

def self_correcting_structured_output(
    llm,
    prompt: str,
    output_schema: Type[T],
    max_retries: int = 3
) -> T:
    last_error = None
    last_output = None

    for attempt in range(max_retries):
        # Sertakan context error dari percobaan sebelumnya
        full_prompt = prompt
        if last_error and last_output:
            full_prompt += f"""

Output sebelumnya tidak valid:
{last_output}

Error yang terjadi:
{last_error}

Perbaiki dan hasilkan output yang valid sesuai schema."""

        raw = llm.invoke(full_prompt).content

        try:
            # Strip markdown jika ada
            json_str = raw.strip().removeprefix("```json").removesuffix("```").strip()
            return output_schema.model_validate_json(json_str)
        except (json.JSONDecodeError, ValidationError) as e:
            last_error = str(e)
            last_output = raw
            print(f"Attempt {attempt+1} failed: {last_error[:100]}...")

    raise RuntimeError(f"Failed after {max_retries} attempts. Last error: {last_error}")

Multi-Agent Debate — Critique-Revise dengan LLM Berbeda

Pola paling powerful tapi juga paling mahal. Dua LLM berbeda berperan sebagai "drafter" dan "adversarial critic." Ini menghindari blind spot yang terjadi ketika LLM mengevaluasi dirinya sendiri.

Python · Multi-Agent Critique-Revise
from langchain_anthropic import ChatAnthropic
from langchain_openai import ChatOpenAI

def multi_agent_debate(
    task: str,
    rounds: int = 2,
    drafter_model: str = "claude-3-5-sonnet-20241022",
    critic_model: str = "gpt-4o"  # Berbeda dari drafter!
) -> str:

    drafter = ChatAnthropic(model=drafter_model)
    # Gunakan LLM berbeda sebagai critic untuk menghindari self-serving bias
    critic = ChatOpenAI(model=critic_model)

    # Initial draft
    draft = drafter.invoke(task).content

    for round_num in range(rounds):
        # Critic memberikan critique yang konstruktif DAN adversarial
        critique = critic.invoke(f"""Anda adalah reviewer yang sangat kritis.
Baca draft berikut dan identifikasi:
1. Asumsi yang tidak berdasar
2. Argumen yang lemah atau tidak lengkap
3. Hal penting yang terlewat
4. Formulasi yang bisa lebih tepat

Bersikaplah konstruktif tapi jangan terlalu sopan — output yang lebih baik
lebih penting dari perasaan "drafter."

Task asli: {task}
Draft: {draft}""").content

        # Drafter revise berdasarkan critique adversarial
        draft = drafter.invoke(f"""Revisi draft Anda berdasarkan critique berikut.
Pertimbangkan setiap poin, tapi Anda boleh tidak setuju jika ada alasan yang kuat.

Critique: {critique}

Draft sebelumnya: {draft}

Hasilkan versi final yang lebih baik:""").content

        print(f"Round {round_num+1} complete. Critique length: {len(critique)} chars")

    return draft

# Cost estimate: 2 rounds = ~4-6 LLM calls = $0.02-0.10 per task
# Worth it only for high-stakes outputs: proposal, analisis penting, keputusan bisnis

Stopping Criteria — Kapan Harus Berhenti

PatternHentikan jika...Jangan terus jika...
ReflectionCritique mengatakan "APPROVED" atau kualitas sudah memenuhi semua kriteriaSudah di iterasi ke-3+ tanpa improvement signifikan — mungkin ada masalah di task definition
Self-CorrectionOutput berhasil di-parse dan validasi schema lulusModel terus menghasilkan error yang sama — mungkin schema terlalu ketat atau prompt perlu diubah
Multi-Agent Debate2 rounds biasanya cukup — diminishing returns setelah ituLebih dari 3 rounds — ini menandakan task definition ambigu atau kriteria tidak jelas
⚠️ Loop Engineering Bukan Pengganti Prompt yang Baik

Kalau output setelah 3 iteration masih tidak memuaskan, masalahnya hampir pasti ada di prompt definition atau task yang terlalu ambigu — bukan di jumlah iteration yang kurang. Lebih banyak loop tidak menyelesaikan masalah fundamental pada prompt atau context.

Gunakan loop engineering dengan hemat: reflection untuk output kualitatif penting, self-correction untuk structured output, multi-agent debate hanya untuk keputusan kritis. Bukan untuk setiap request.

Kesimpulan

Tiga pola loop engineering dengan use case yang jelas: Reflection untuk dokumen dan konten yang butuh kualitas tinggi, Self-Correction untuk output terstruktur yang bisa divalidasi programatik, Multi-Agent Debate untuk keputusan atau analisis yang paling kritis. Selalu set max_iterations dan jangan percaya bahwa lebih banyak loop = lebih baik.