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
Reflection Loop — Implementasi Penuh
Reflection loop adalah pola paling sederhana: LLM mengevaluasi output-nya sendiri, identifikasi kekurangan, dan revisi. Tiga langkah dalam satu agen:
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:
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.
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
| Pattern | Hentikan jika... | Jangan terus jika... |
|---|---|---|
| Reflection | Critique mengatakan "APPROVED" atau kualitas sudah memenuhi semua kriteria | Sudah di iterasi ke-3+ tanpa improvement signifikan — mungkin ada masalah di task definition |
| Self-Correction | Output berhasil di-parse dan validasi schema lulus | Model terus menghasilkan error yang sama — mungkin schema terlalu ketat atau prompt perlu diubah |
| Multi-Agent Debate | 2 rounds biasanya cukup — diminishing returns setelah itu | Lebih dari 3 rounds — ini menandakan task definition ambigu atau kriteria tidak jelas |
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.