التعافي من الأخطاء للوكلاء
كيف يتعامل وكلاء LLM مع الأخطاء ويتعافون منها — إعادة المحاولة، التخزين، التعلم.
التعافي من الأخطاء للوكلاء
الأخطاء تحدث. الشبكات تفشل، APIs تُرجع 500، Mind Keys تنتهي. يُظهر هذا الدليل كيف ينبغي لوكلاء LLM التعامل مع الأخطاء بلطف والتعلّم منها.
مبادئ معالجة الأخطاء
- أعد المحاولة مع تراجع — الأخطاء العابرة تُحل غالبًا
- خزّن الخطأ — تعلّم من الأنماط
- تدهور بلطف — لا تُعطّل الجلسة كاملة
- أبلغ الإنسان — للأخطاء التي لا تستطيع حلها
معالجة أخطاء HTTP
إعادة المحاولة مع تراجع أُسي
import time
import requests
def call_with_retry(url, max_retries=3, backoff_base=2):
"""Call URL with exponential backoff."""
for attempt in range(max_retries):
try:
r = requests.get(url, headers={"Authorization": f"Bearer {KEY}"})
# نجاح
if r.status_code < 400:
return r
# حد المعدل — انتظر وأعد المحاولة
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", 60))
print(f"Rate limited. Waiting {wait}s...")
time.sleep(wait)
continue
# خطأ خادم — أعد المحاولة مع تراجع
if r.status_code >= 500:
wait = backoff_base ** attempt
print(f"Server error {r.status_code}. Retrying in {wait}s...")
time.sleep(wait)
continue
# خطأ عميل — لا تعد المحاولة
if 400 <= r.status_code < 500:
raise ClientError(f"{r.status_code}: {r.text}")
except requests.RequestException as e:
# خطأ شبكة — أعد المحاولة
wait = backoff_base ** attempt
print(f"Network error: {e}. Retrying in {wait}s...")
time.sleep(wait)
raise MaxRetriesError(f"Failed after {max_retries} retries")معالجة خطأ المصادقة
def safe_call(url):
"""Call with auth error handling."""
try:
return call_with_retry(url)
except ClientError as e:
if "401" in str(e):
# Mind Key غير صالح — حرج، لا يمكن التعافي
store_error("auth_invalid", str(e), "Check Mind Key")
notify_human("My Mind Key is invalid. Please update.")
raise AuthError("Cannot continue without valid Mind Key")
elif "403" in str(e):
store_error("forbidden", str(e), "Wrong token type?")
raise ForbiddenError(str(e))
elif "404" in str(e):
# المسار غير موجود — لا تعد المحاولة
store_error("not_found", str(e), "Check endpoint path")
raise NotFoundError(str(e))
else:
raiseتخزين الأخطاء كذكريات
عند وقوع أخطاء، خزّنها حتى تتعلّم الجلسات المستقبلية:
def store_error(error_type, error_message, recovery_hint=""):
"""Store an error as a memory for future reference."""
requests.post(f"{URL}/memory",
headers={"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json"},
json={
"category": "mistake",
"key": f"error_{error_type}_{int(time.time())}",
"content": f"Error: {error_type}\nMessage: {error_message}\nRecovery: {recovery_hint}",
"tags": ["error", error_type],
"priority": "high"
})
# مثال
try:
deploy()
except DeployError as e:
store_error("deploy_failed", str(e),
"Check CI logs, verify Docker image exists")
raiseسيناريوهات أخطاء شائعة
السيناريو 1: Mind Key غير صالح
# الكشف: 401 على كل استدعاء
# التعافي: لا يمكن التعافي — يحتاج تدخل إنسان
def handle_invalid_mind_key():
store_error("mind_key_invalid",
"All API calls returning 401",
"Mind Key may be revoked. Need new key.")
# أبلغ الإنسان عبر الدردشة (إن أمكن)
try:
reply("⚠️ My Mind Key is invalid. I cannot access memories. "
"Please check and update SYNAPSE_MIND_KEY.")
except:
pass # قد تفشل الدردشة أيضًا بمفتاح سيء
# اخرج بلطف
raise CriticalError("Cannot continue without valid Mind Key")السيناريو 2: خطأ شبكة
# الكشف: ConnectionError، Timeout
# التعافي: أعد المحاولة مع تراجع، ثم تدهور
def handle_network_error(url, retry=3):
for attempt in range(retry):
try:
return requests.get(url, timeout=10)
except (requests.ConnectionError, requests.Timeout) as e:
wait = 2 ** attempt
print(f"Network error, retrying in {wait}s: {e}")
time.sleep(wait)
# فشلت جميع المحاولات — تدهور
store_error("network_failure",
f"Cannot reach {url}",
"Check internet connection, Synapse may be down")
# اعمل دون اتصال إن أمكن
return work_offline()السيناريو 3: محدود بحد المعدل
# الكشف: 429 مع ترويسة Retry-After
# التعافي: انتظر وأعد المحاولة، أو بدّل إلى مصادقة الترويسة
def handle_rate_limit(response):
retry_after = int(response.headers.get("Retry-After", 60))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
# إذا استمر هذا، اقترح التبديل إلى مصادقة الترويسة
if has_query_param_auth(url):
store_error("rate_limited",
"Frequent 429s with ?key= auth",
"Switch to Authorization: Bearer header (no rate limit)")السيناريو 4: خطأ خادم (5xx)
# الكشف: 500، 502، 503
# التعافي: أعد المحاولة مع تراجع، تحقق من /health
def handle_server_error(url):
# تحقق إذا كان الخادم يعمل
health = requests.get(f"{URL}/health")
if health.status_code != 200:
store_error("server_down",
"Synapse health check failing",
"Wait for server recovery")
raise ServerDownError()
# أعد المحاولة مع تراجع
return call_with_retry(url, max_retries=5)السيناريو 5: فشل استدعاء أداة
# الكشف: الأداة تُرجع محتوى خطأ
# التعافي: جرب نهجًا بديلًا، خزّن الفشل
def call_tool_safely(tool_name, args, alternatives=None):
try:
result = call_tool(tool_name, args)
if result.get("isError"):
raise ToolError(result["content"])
return result
except ToolError as e:
store_error(f"tool_{tool_name}_failed",
f"Args: {args}\nError: {e}",
f"Try: {alternatives or 'no alternatives'}")
# جرب البدائل
if alternatives:
for alt in alternatives:
try:
return call_tool(alt, args)
except:
continue
raiseالنمط: قاطع الدائرة
للإخفاقات المتكررة، توقف عن المحاولة مؤقتًا:
class CircuitBreaker:
def __init__(self, threshold=5, reset_time=300):
self.failures = 0
self.threshold = threshold
self.reset_time = reset_time
self.last_failure = 0
def call(self, fn, *args, **kwargs):
if self.failures >= self.threshold:
if time.time() - self.last_failure < self.reset_time:
raise CircuitOpenError("Circuit breaker open")
else:
# إعادة تعيين
self.failures = 0
try:
result = fn(*args, **kwargs)
self.failures = 0 # إعادة تعيين عند النجاح
return result
except:
self.failures += 1
self.last_failure = time.time()
raise
# الاستخدام
breaker = CircuitBreaker(threshold=5)
try:
result = breaker.call(api_call, url)
except CircuitOpenError:
print("Too many failures, waiting before retry")