حفظ نقاط التحقق واستئناف المهام
احفظ حالة الوكيل بعد كل خطوة مكتملة، حتى يمكن استئناف المهمة طويلة التنفيذ من آخر نقطة تحقق ناجحة بدلًا من البدء من الصفر بعد حدوث فشل.
حفظ نقاط التحقق واستئناف المهام درس مجاني في AI Engineering Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Engineering Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.
مشكلة الوكلاء طويلي التشغيل
قد تستغرق مهمة بحثية ينفذها وكيل على 50 خطوة مدة 30 دقيقة. وإذا فشل الوكيل عند الخطوة 47 بسبب انتهاء مهلة واجهة API أو إعادة تشغيل الخادم، فإن البدء من الصفر يهدر كل العمل المنجز ويستهلك رموزًا إضافية. يحفظ تحديد نقاط الفحص حالة الوكيل عند إكمال كل خطوة، بحيث يمكن استئناف المهمة من آخر نقطة ناجحة بدلًا من البداية. وهذا ضروري لأي مهمة وكيل تستغرق أكثر من بضع دقائق.
ما حالة الوكيل التي ينبغي حفظها
تتكوّن حالة الوكيل من: تعريف المهمة، والخطوات المكتملة مع استدعاءات الأدوات وملاحظاتها، وفهرس الخطوة الحالية، وأي نتائج متراكمة مثل الملفات المكتوبة والبيانات المجمعة، إضافةً إلى بيانات وصفية مثل وقت البدء وإجمالي استخدام الرموز. احفظوا كل ذلك بعد إكمال كل خطوة. يجب أن تكون الحالة قابلة للتسلسل — لذا يُفضّل استخدام JSON بدلًا من كائنات Python لضمان قابلية النقل.
from dataclasses import dataclass, field
from typing import List, Any, Optional
@dataclass
class AgentStep:
step_index: int
thought: str
tool_name: str
tool_args: dict
observation: str
tokens_used: int
completed_at: str
@dataclass
class AgentCheckpoint:
task_id: str
task_description: str
status: str # 'running', 'completed', 'failed'
current_step: int
completed_steps: List[AgentStep] = field(default_factory=list)
accumulated_results: dict = field(default_factory=dict)
total_tokens: int = 0
final_answer: Optional[str] = Noneحفظ نقاط الفحص بعد كل خطوة
بعد كل خطوة ناجحة، نفّذوا تسلسل نقطة الفحص واكتبوها في مخزن دائم. استخدموا قاعدة بيانات أو تخزين كائنات مثل S3 وRedis بدلًا من القرص المحلي، حتى تبقى نقطة الفحص محفوظة بعد إعادة تشغيل الخادم. أدرجوا task_id في المفتاح حتى تتمكنوا من استرداد نقطة فحص مهمة محددة. اكتبوا أيضًا إدخالًا في سجل الخطوات لكل خطوة للحفاظ على مسار تدقيق حتى إذا تلف ملف نقطة الفحص.
import json
import redis
from dataclasses import asdict
redis_client = redis.Redis()
def save_checkpoint(checkpoint: AgentCheckpoint):
key = f'agent:checkpoint:{checkpoint.task_id}'
data = json.dumps(asdict(checkpoint), default=str)
redis_client.set(key, data, ex=86400) # 24h TTL
# Also append to step log
log_key = f'agent:log:{checkpoint.task_id}'
if checkpoint.completed_steps:
last = checkpoint.completed_steps[-1]
redis_client.rpush(log_key, json.dumps(asdict(last), default=str))
def load_checkpoint(task_id: str) -> AgentCheckpoint | None:
key = f'agent:checkpoint:{task_id}'
data = redis_client.get(key)
if data:
return AgentCheckpoint(**json.loads(data))
return Noneالاستئناف من نقطة فحص
عند استئناف مهمة، حمّلوا نقطة الفحص وأعيدوا بناء سجل رسائل الوكيل من الخطوات المكتملة. ابدؤوا حلقة التنفيذ من فهرس الخطوة التالية غير المكتملة. يتلقى النموذج أفكاره وملاحظاته السابقة ضمن سجل الرسائل، لذلك يملك السياق الكامل لما أُنجز، مما يتيح له المتابعة دون تكرار العمل.
async def resume_or_start(task_id: str, task_description: str) -> str:
checkpoint = load_checkpoint(task_id)
if checkpoint and checkpoint.status == 'running':
print(f'Resuming task {task_id} from step {checkpoint.current_step}')
messages = rebuild_history(checkpoint)
start_step = checkpoint.current_step
else:
print(f'Starting new task {task_id}')
checkpoint = AgentCheckpoint(task_id=task_id, task_description=task_description, status='running', current_step=0)
messages = [{'role': 'user', 'content': task_description}]
start_step = 0
save_checkpoint(checkpoint)
return await run_agent_from(checkpoint, messages, start_step)إعادة بناء سجل الرسائل من الخطوات
يكمن مفتاح الاستئناف في إعادة بناء سجل الرسائل بأمانة من الخطوات المحفوظة. تقابل كل خطوة مكتملة رسالةً من المساعد، تتضمن الفكرة واستدعاء الأداة، ورسالةً من الأداة تتضمن الملاحظة. أعيدوا تشغيل جميع الخطوات المكتملة بوصفها رسائل قبل المتابعة، حتى يملك النموذج السياق نفسه كما لو لم يتعرض لانقطاع.
def rebuild_history(checkpoint: AgentCheckpoint) -> list:
messages = [{'role': 'user', 'content': checkpoint.task_description}]
for step in checkpoint.completed_steps:
# Reconstruct the agent's reasoning message
messages.append({
'role': 'assistant',
'content': f'Thought: {step.thought}\nAction: {step.tool_name}({step.tool_args})'
})
# Reconstruct the tool observation
messages.append({
'role': 'user',
'content': f'Observation: {step.observation}'
})
return messagesاستدعاءات الأدوات القابلة للتكرار بأمان
إذا اكتملت خطوة جزئيًا قبل حدوث عطل، أي جرى استدعاء الأداة دون حفظ الملاحظة، فقد يستدعي الاستئناف الأداة مرة أخرى. صمّموا الأدوات بحيث تكون قابلة للتكرار بأمان: يؤدي استدعاؤها مرتين بالوسيطات نفسها إلى النتيجة نفسها الناتجة عن استدعائها مرة واحدة. وبالنسبة إلى عمليات الكتابة، مثل إنشاء الملفات وإرسال رسائل البريد الإلكتروني، استخدموا مفاتيح إزالة التكرار لمنع الآثار المكررة حتى إذا استُدعيت الأداة عدة مرات.
async def idempotent_write_file(content: str, path: str, task_id: str, step: int) -> str:
dedup_key = f'{task_id}:step_{step}:write:{path}'
if redis_client.exists(dedup_key):
return f'File {path} already written (dedup key present)'
with open(path, 'w') as f:
f.write(content)
redis_client.set(dedup_key, '1', ex=3600)
return f'Successfully wrote {len(content)} chars to {path}'تنظيف نقاط الفحص والاحتفاظ بها
تستهلك نقاط الفحص مساحة تخزين، ولا ينبغي أن تتراكم إلى أجل غير مسمى. حدّدوا سياسة للاحتفاظ: احذفوا نقاط الفحص المكتملة بعد 24 ساعة، ونقاط الفحص الفاشلة بعد 7 أيام، لأغراض تحليل ما بعد الحادث، ولا تحذفوا نقاط الفحص قيد التشغيل تلقائيًا مطلقًا. نفّذوا مهمة تنظيف في الخلفية تعمل كل ساعة وتحذف نقاط الفحص المنتهية وفقًا للسياسة.
from datetime import datetime, timedelta
RETENTION = {
'completed': timedelta(hours=24),
'failed': timedelta(days=7),
'running': None # never auto-delete
}
def cleanup_expired_checkpoints():
now = datetime.utcnow()
for key in redis_client.scan_iter('agent:checkpoint:*'):
data = json.loads(redis_client.get(key))
status = data.get('status', 'running')
retention = RETENTION.get(status)
if retention is None:
continue
started = datetime.fromisoformat(data.get('started_at', str(now)))
if now - started > retention:
redis_client.delete(key)تحديد نقاط الفحص في LangGraph
يوفّر LangGraph دعمًا أصليًا لتحديد نقاط الفحص عبر صفي MemorySaver وSqliteSaver. أرفقوا أداة حفظ نقاط الفحص بالرسم البياني، وسيُحفظ تنفيذ كل عقدة تلقائيًا. للاستئناف، استدعوا graph.invoke باستخدام thread_id نفسه. يتولى LangGraph إعادة بناء السجل وتتبع الخطوات، لذلك لا تحتاجون إلى تنفيذ منطق تحديد نقاط الفحص يدويًا.
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import StateGraph
# Create graph with persistent checkpointer
checkpointer = SqliteSaver.from_conn_string('/tmp/agent_state.db')
graph = StateGraph(AgentState)
graph.add_node('reason', reason_node)
graph.add_node('act', act_node)
# ... add edges ...
app = graph.compile(checkpointer=checkpointer)
# Run with thread_id - LangGraph auto-checkpoints
config = {'configurable': {'thread_id': 'task_abc123'}}
result = await app.ainvoke({'task': 'Research climate change'}, config)
# Resume same thread - LangGraph loads from checkpoint
result = await app.ainvoke({'task': 'Continue'}, config)تحديد نقاط الفحص الموزعة للوكلاء المتوازيين
عندما يعمل عدة وكلاء بالتوازي على مهام فرعية، يحتاج كل وكيل إلى مساحة أسماء خاصة بنقاط الفحص. استخدموا بنية مفاتيح هرمية: parent_task_id:sub_task_id. تسجل نقطة فحص الوكيل الأب المهام الفرعية المكتملة ونتائجها. وعند استئناف الوكيل الأب، يعيد استخدام نتائج المهام الفرعية المكتملة من نقطة الفحص بدلًا من تشغيلها مرة أخرى.
async def parallel_with_checkpoints(parent_id: str, subtasks: list) -> list:
results = []
for i, subtask in enumerate(subtasks):
sub_id = f'{parent_id}:sub_{i}'
# Check if subtask already completed
existing = load_checkpoint(sub_id)
if existing and existing.status == 'completed':
print(f'Sub-task {i} already done, using cached result')
results.append(existing.final_answer)
else:
result = await run_agent(sub_id, subtask)
results.append(result)
return resultsاختبار سلوك الاستئناف
اكتبوا اختبارات تكامل تتسبب عمدًا في تعطل الوكيل أثناء المهمة، وتحققوا من أن الاستئناف ينتج النتيجة النهائية الصحيحة. حاكوا التعطل عبر رفع استثناء عند فهرس خطوة محدد. بعد الاستئناف، تحققوا من إعادة تنفيذ الخطوات التي تلي التعطل فقط، لا الخطوات السابقة له. اختبروا أيضًا أن استدعاءات الأدوات القابلة للتكرار بأمان لا تنتج آثارًا جانبية مكررة عند إعادة تشغيل خطوة.
import pytest
@pytest.mark.asyncio
async def test_resumption_from_step_3():
task_id = 'test_resume_001'
# Run until step 3, then crash
with pytest.raises(SimulatedCrash):
await run_agent_crashing_at(task_id, 'Research AI trends', crash_at_step=3)
checkpoint = load_checkpoint(task_id)
assert checkpoint.current_step == 3
assert len(checkpoint.completed_steps) == 3
# Resume and complete
result = await resume_or_start(task_id, 'Research AI trends')
assert result is not None
# Verify only steps 4+ were re-executed
assert checkpoint_step_was_not_replayed(task_id, step=0)إصدار نقاط الفحص عند تغيّر المخطط
عند تغيير مخطط AgentCheckpoint، مثل إضافة الحقول أو إعادة تسميتها، تصبح نقاط الفحص القديمة في التخزين غير متوافقة. عالجوا ذلك باستخدام إصدار نقاط الفحص: أضيفوا الحقل checkpoint_version واكتبوا دوال ترحيل لترقية نقاط الفحص القديمة إلى المخطط الجديد عند تحميلها. يمنع ذلك حدوث أعطال عند استئناف الوكلاء بعد نشر غيّر تنسيق نقطة الفحص.
def load_and_migrate_checkpoint(task_id: str) -> AgentCheckpoint:
raw = json.loads(redis_client.get(f'agent:checkpoint:{task_id}'))
version = raw.get('checkpoint_version', '1.0')
if version == '1.0':
# Migrate: add new fields added in v2.0
raw['checkpoint_version'] = '2.0'
raw['accumulated_results'] = raw.get('accumulated_results', {})
raw['total_tokens'] = raw.get('total_tokens', 0)
return AgentCheckpoint(**raw)تحقق سريع
اختبروا فهمكم لتحديد نقاط الفحص واستئناف المهام في الوكلاء.
مراجعة الدرس
تعلّمتم في هذا الدرس أن تحديد نقاط الفحص يُسلسل حالة الوكيل بعد كل خطوة، مما يتيح للمهام طويلة التشغيل تجاوز الأعطال دون البدء من الصفر، وأن إعادة بناء سجل الرسائل من الخطوات المحفوظة تمنح النموذج السياق الكامل عند الاستئناف، وأن الأدوات القابلة للتكرار بأمان مع مفاتيح إزالة التكرار تمنع الآثار الجانبية المكررة عند إعادة تشغيل الخطوات. سننتقل بعد ذلك إلى تصميم مُشغّلات التصعيد التي تتضمن الإنسان في الحلقة.
الأسئلة الشائعة
هل درس «حفظ نقاط التحقق واستئناف المهام» مجاني؟
نعم — نص درس «حفظ نقاط التحقق واستئناف المهام» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Engineering Academy، انتقل إلى CoddyKit PRO. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.
ماذا ستتعلم في «حفظ نقاط التحقق واستئناف المهام»؟
احفظ حالة الوكيل بعد كل خطوة مكتملة، حتى يمكن استئناف المهمة طويلة التنفيذ من آخر نقطة تحقق ناجحة بدلًا من البدء من الصفر بعد حدوث فشل. تتمرن على AI Engineering Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ AI Engineering Academy؟
لا تُشترط خبرة سابقة. AI Engineering Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «حفظ نقاط التحقق واستئناف المهام»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس AI Engineering Academy هذا؟
نعم. كل درس في AI Engineering Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تصنيف أنماط فشل الوكلاء
- التصحيح الذاتي وكتابة Prompts تأملية
- حفظ نقاط التحقق واستئناف المهام
- التصعيد إلى الإنسان ضمن الحلقة