कंप्यूटर एक्सेस सिस्टम

एजेंट एरर रिकवरी: प्रोडक्शन विश्वसनीयता के लिए 5 पैटर्न


आपका एजेंट टेस्टिंग में परफेक्ट काम कर रहा था। फिर प्रोडक्शन में, एक मल्टी-स्टेप वर्कफ्लो के चरण 3 पर रेट लिमिट से टकरा गया, एक अनकैप्चर्ड एक्सेप्शन फेंका, और आपके सिस्टम को अनडिफाइंड स्टेट में छोड़ दिया। कोई चेकपॉइंट नहीं, कोई रिट्राई नहीं, कोई फॉलबैक नहीं। बस खामोशी—और एक टूटा हुआ पाइपलाइन जिसे मैन्युअली रीस्टार्ट करना होगा।

एजेंट एरर रिकवरी एक डेमो और प्रोडक्शन सिस्टम के बीच का अंतर है। यह आर्टिकल प्रोडक्शन एजेंटिक वर्कफ्लो में उपयोग किए जाने वाले पांच पैटर्न कवर करता है: एक्सपोनेंशियल बैकऑफ, सर्किट ब्रेकर, चेकपॉइंट-और-रिज्यूम, फॉलबैक स्ट्रेटेजी और एस्केलेशन क्यू। हर पैटर्न Anthropic SDK के साथ इम्प्लीमेंट किया गया है।

पूर्वापेक्षाएं: Python और Claude API की बुनियादी जानकारी।


एजेंट पारंपरिक सॉफ्टवेयर से अलग तरह से क्यों फेल होते हैं

एजेंट अधिक जटिल तरीकों से फेल होते हैं:

  • आंशिक प्रगति: एजेंट 8-चरण वर्कफ्लो के चरण 1–4 पूरे करके फेल हो जाता है। रिकवरी के बिना सारी प्रगति खो जाती है।
  • अस्पष्ट स्थिति: एजेंट ने एक टूल कॉल किया, लेकिन रिस्पॉन्स मालफॉर्म्ड था। क्या एक्शन हुआ? रिट्राई करना चाहिए?
  • कैस्केडिंग फेलियर: एक धीमी API कॉल पूरे रीजनिंग लूप को ब्लॉक कर देती है।
  • साइलेंट फेलियर: LLM रिस्पॉन्स देता है, लेकिन एक्सपेक्टेड फॉर्मेट फॉलो नहीं करता। डाउनस्ट्रीम पार्सर साइलेंटली फेल हो जाता है।

पैटर्न 1: जिटर के साथ एक्सपोनेंशियल बैकऑफ

एक्सपोनेंशियल बैकऑफ रिट्राई के बीच वेट टाइम को दोगुना करता है। जिटर रैंडमनेस जोड़ता है ताकि कई एजेंट एक साथ रिट्राई न करें।

import anthropic
import time
import random
from typing import Optional
client = anthropic.Anthropic()
def call_with_backoff(
messages: list,
model: str = "claude-sonnet-4-6",
max_retries: int = 5,
base_delay: float = 1.0,
max_delay: float = 60.0,
) -> Optional[anthropic.types.Message]:
"""
ट्रांजिएंट एरर पर एक्सपोनेंशियल बैकऑफ के साथ Claude API कॉल करें।
रेट लिमिट और सर्वर एरर पर रिट्राई; क्लाइंट एरर पर तुरंत raise करें।
"""
for attempt in range(max_retries):
try:
return client.messages.create(
model=model,
max_tokens=1024,
messages=messages,
)
except anthropic.RateLimitError as e:
if attempt == max_retries - 1:
raise
delay = min(base_delay * (2 ** attempt), max_delay)
jitter = random.uniform(0, delay)
print(f"रेट लिमिट। {jitter:.1f}s में रिट्राई (प्रयास {attempt + 1}/{max_retries})")
time.sleep(jitter)
except anthropic.APIStatusError as e:
if e.status_code < 500:
raise
if attempt == max_retries - 1:
raise
delay = min(base_delay * (2 ** attempt), max_delay)
jitter = random.uniform(0, delay)
print(f"सर्वर एरर {e.status_code}{jitter:.1f}s में रिट्राई")
time.sleep(jitter)
except anthropic.APIConnectionError:
if attempt == max_retries - 1:
raise
delay = min(base_delay * (2 ** attempt), max_delay)
time.sleep(delay)
return None

कब उपयोग करें: एजेंट लूप के अंदर किसी भी API कॉल पर। सभी LLM कॉल के लिए डिफॉल्ट रैपर होना चाहिए।


पैटर्न 2: सर्किट ब्रेकर

सर्किट ब्रेकर फेलियर रेट ट्रैक करता है और फेल हो रही सर्विस को कॉल करना अस्थायी रूप से बंद कर देता है। तीन स्थितियां: क्लोज्ड (सामान्य), ओपन (फेलियर), हाफ-ओपन (रिकवरी)।

import time
from enum import Enum
from dataclasses import dataclass, field
from typing import Callable, TypeVar, Any
T = TypeVar("T")
class CircuitState(Enum):
CLOSED = "closed"
OPEN = "open"
HALF_OPEN = "half_open"
@dataclass
class CircuitBreaker:
failure_threshold: int = 5 # ओपन होने से पहले लगातार विफलताएं
recovery_timeout: float = 60.0 # हाफ-ओपन ट्राई से पहले सेकंड
success_threshold: int = 2 # हाफ-ओपन से क्लोज होने के लिए सफलताएं
_state: CircuitState = field(default=CircuitState.CLOSED, init=False)
_failure_count: int = field(default=0, init=False)
_success_count: int = field(default=0, init=False)
_last_failure_time: float = field(default=0.0, init=False)
@property
def state(self) -> CircuitState:
if self._state == CircuitState.OPEN:
if time.time() - self._last_failure_time > self.recovery_timeout:
self._state = CircuitState.HALF_OPEN
self._success_count = 0
return self._state
def call(self, func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
if self.state == CircuitState.OPEN:
raise RuntimeError("सर्किट ब्रेकर ओपन है — सर्विस अनुपलब्ध")
try:
result = func(*args, **kwargs)
self._on_success()
return result
except Exception:
self._on_failure()
raise
def _on_success(self) -> None:
self._failure_count = 0
if self._state == CircuitState.HALF_OPEN:
self._success_count += 1
if self._success_count >= self.success_threshold:
self._state = CircuitState.CLOSED
def _on_failure(self) -> None:
self._failure_count += 1
self._last_failure_time = time.time()
if self._failure_count >= self.failure_threshold:
self._state = CircuitState.OPEN

पैटर्न 3: चेकपॉइंट और रिज्यूम

लंबे समय तक चलने वाले एजेंट को फेलियर के बाद आखिरी सफल स्टेप से रिज्यूम करने में सक्षम होना चाहिए।

import json
import os
from dataclasses import dataclass, asdict, field
from typing import Optional
@dataclass
class AgentCheckpoint:
task_id: str
current_step: int
completed_steps: list[str] = field(default_factory=list)
results: dict = field(default_factory=dict)
messages: list[dict] = field(default_factory=list)
def save(self, directory: str = ".checkpoints") -> None:
os.makedirs(directory, exist_ok=True)
path = os.path.join(directory, f"{self.task_id}.json")
with open(path, "w") as f:
json.dump(asdict(self), f, indent=2)
@classmethod
def load(cls, task_id: str, directory: str = ".checkpoints") -> Optional["AgentCheckpoint"]:
path = os.path.join(directory, f"{task_id}.json")
if not os.path.exists(path):
return None
with open(path) as f:
data = json.load(f)
return cls(**data)
def clear(self, directory: str = ".checkpoints") -> None:
path = os.path.join(directory, f"{self.task_id}.json")
if os.path.exists(path):
os.remove(path)

यह पैटर्न LangGraph के बिल्ट-इन MemorySaver और PostgresSaver चेकपॉइंटर्स के साथ स्वाभाविक रूप से इंटीग्रेट होता है। देखें LangGraph के साथ स्टेट मशीन और एजेंट


पैटर्न 4: फॉलबैक स्ट्रेटेजी

कुछ फेलियर रिट्राई योग्य नहीं होते। आपको एक वैकल्पिक पथ चाहिए जो एजेंट को आगे बढ़ाता रहे।

from typing import Callable
def with_fallback(
primary: Callable[[], str],
fallback: Callable[[], str],
error_types: tuple = (Exception,),
) -> str:
"""प्राइमरी फंक्शन ट्राई करें; निर्दिष्ट एरर पर फॉलबैक का उपयोग करें।"""
try:
return primary()
except error_types as e:
print(f"प्राइमरी फेल ({type(e).__name__}): {e}। फॉलबैक उपयोग कर रहे हैं।")
return fallback()
def answer_question(question: str) -> str:
def primary():
# प्राइमरी: सबसे सक्षम मॉडल
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": question}],
)
return response.content[0].text
def fallback():
# फॉलबैक: हल्का और तेज मॉडल
response = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=512,
messages=[{"role": "user", "content": question}],
)
return f"[फॉलबैक रिस्पॉन्स] {response.content[0].text}"
return with_fallback(
primary, fallback,
error_types=(anthropic.RateLimitError, anthropic.APIStatusError),
)

पैटर्न 5: एस्केलेशन क्यू

कुछ फेलियर वास्तव में स्वचालित रूप से हल नहीं की जा सकती। एस्केलेशन क्यू विफल टास्क को इतने संदर्भ के साथ कैप्चर करता है कि कोई मानव उन्हें हल कर सके।

import json
import uuid
from datetime import datetime
from dataclasses import dataclass, asdict
from enum import Enum
class EscalationReason(Enum):
MAX_RETRIES_EXCEEDED = "max_retries_exceeded"
AMBIGUOUS_STATE = "ambiguous_state"
LOW_CONFIDENCE = "low_confidence"
REQUIRES_HUMAN = "requires_human"
IRREVERSIBLE_ACTION = "irreversible_action"
@dataclass
class EscalationRecord:
id: str
task_id: str
reason: str
error_message: str
agent_state: dict
context: str
timestamp: str
resolved: bool = False
def save(self, queue_file: str = "escalation_queue.jsonl") -> None:
with open(queue_file, "a") as f:
f.write(json.dumps(asdict(self), ensure_ascii=False) + "\n")
def escalate(
task_id: str,
reason: EscalationReason,
error_message: str,
agent_state: dict,
context: str = "",
) -> EscalationRecord:
record = EscalationRecord(
id=str(uuid.uuid4()),
task_id=task_id,
reason=reason.value,
error_message=error_message,
agent_state=agent_state,
context=context,
timestamp=datetime.utcnow().isoformat(),
)
record.save()
print(f"[एस्केलेट किया गया] टास्क {task_id}: {reason.value}")
return record

कब उपयोग करें: अपरिवर्तनीय एक्शन या कम कॉन्फिडेंस डिसीजन वाले टास्क के लिए। एस्केलेशन क्यू को हैंडल करने वाले सुपरवाइजर एजेंट के लिए देखें मल्टी-एजेंट पैटर्न


सामान्य गलतियां

गलती 1: नॉन-रिट्राईएबल एरर को रिट्राई करना

समाधान: रिट्राई से पहले एरर को क्लासिफाई करें। केवल ट्रांजिएंट एरर रिट्राई करें।

# गलत: सब कुछ कैच करता है
except Exception:
retry()
# सही: केवल रिट्राईएबल एरर
except (anthropic.RateLimitError, anthropic.APIConnectionError):
retry()
except anthropic.APIStatusError as e:
if e.status_code >= 500:
retry()
else:
raise

गलती 2: रिट्राई पर स्टेट खोना

समाधान: रिट्राई के बीच मैसेज हिस्ट्री प्रिजर्व करें। केवल फेल हुए स्टेप को रिट्राई करें।

गलती 3: अनबाउंडेड रिट्राई लूप

समाधान: हमेशा max_retries सेट करें। एग्जॉस्ट होने के बाद एस्केलेट करें या फास्ट फेल करें।


प्रोडक्शन डिप्लॉयमेंट चेकलिस्ट

  • सभी API कॉल एक्सपोनेंशियल बैकऑफ से रैप्ड (बेस 1s, मैक्स 60s, जिटर इनेबल्ड)
  • हर एक्सटर्नल डिपेंडेंसी पर सर्किट ब्रेकर
  • हर महत्वपूर्ण स्टेप के बाद ड्यूरेबल स्टोरेज में चेकपॉइंट सेव
  • फॉलबैक पाथ टेस्ट किए और रिस्पॉन्स मेटाडेटा में मार्क किए
  • एस्केलेशन क्यू हर दिन मॉनिटर और रिव्यू
  • रिट्राई काउंट बाउंडेड (max_retries ≤ 5)

अगले कदम

  1. बैकऑफ से शुरू करें — हर API कॉल को call_with_backoff में रैप करें।
  2. चेकपॉइंट जोड़ें किसी भी 2–3 से अधिक स्टेप वाले वर्कफ्लो में।
  3. एस्केलेशन क्यू बनाएं अपरिवर्तनीय एक्शन वाले टास्क के लिए।
  4. फेलियर इंजेक्शन टेस्ट लिखें — हर एरर टाइप को जानबूझकर ट्रिगर करके रिकवरी पाथ टेस्ट करें।

संबंधित गाइड:


संबंधित लेख