الذاكرة المشتركة والتواصل بين الوكلاء
نفذوا طبقة ذاكرة مشتركة باستخدام مخزن مفتاح-قيمة يقرأ منه الوكلاء ويكتبون إليه، مما يتيح التعاون غير المتزامن دون اقتران وثيق بين الوكلاء.
الذاكرة المشتركة والتواصل بين الوكلاء درس مجاني في AI Engineering Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Engineering Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.
مشكلة عزل الوكلاء
في النظام متعدد الوكلاء، يعمل كل وكيل في سياقه الخاص ولا يملك أي معرفة بما يفعله الوكلاء الآخرون أو بما أنجزوه. فإذا اكتشف وكيل الباحث حقيقة مهمة، فكيف يعرف بها وكيل الكاتب؟ وكيف يعرف وكيل المراقبة أن وكيل البرمجة واجه خطأً؟ من دون آلية تواصل مشتركة، يصبح الوكلاء جزرًا معزولة لا يمكنها التعاون بفاعلية.
الذاكرة المشتركة: نموذج السبورة
يتمثل الحل الكلاسيكي لتواصل الوكلاء متعددي الأطراف في نموذج السبورة: مخزن بيانات مشترك (السبورة) يمكن لأي وكيل القراءة منه أو الكتابة فيه. ينشر الوكلاء نتائجهم، ويقرؤون مساهمات الآخرين، وينسقون ضمنيًا عبر الحالة المشتركة. ويفصل هذا النموذج الوكلاء عن بعضهم — فلا يحتاجون إلى معرفة وجود بعضهم، بل إلى معرفة بنية الذاكرة المشتركة فقط.
# Simple in-memory blackboard using a dictionary
from threading import Lock
class Blackboard:
def __init__(self):
self._data = {}
self._lock = Lock() # thread-safe for parallel agents
def write(self, key: str, value, agent_id: str):
with self._lock:
self._data[key] = {'value': value, 'written_by': agent_id}
print(f'[{agent_id}] wrote: {key}')
def read(self, key: str):
with self._lock:
return self._data.get(key, {}).get('value')
def keys(self):
with self._lock:
return list(self._data.keys())
blackboard = Blackboard()الذاكرة المشتركة المستمرة باستخدام Redis
في الأنظمة متعددة الوكلاء المخصصة للإنتاج، حيث تعمل الوكلاء كعمليات أو خدمات منفصلة، لا تكون القواميس الموجودة في الذاكرة كافية. ويُعد Redis الخيار الأكثر شيوعًا لذاكرة الوكلاء المشتركة: فهو سريع، ويدعم أنواع بيانات غنية (السلاسل، والتجزئات، والقوائم، والمجموعات المرتبة)، ويحتوي على TTL مدمج لانتهاء الصلاحية، ويتعامل بأمان مع عمليات القراءة والكتابة المتزامنة باستخدام العمليات الذرية.
import redis
import json
class RedisSharedMemory:
def __init__(self, prefix='agent:'):
self.redis = redis.Redis(host='localhost', port=6379, decode_responses=True)
self.prefix = prefix
def set(self, key: str, value, ttl_seconds=3600):
full_key = self.prefix + key
self.redis.setex(full_key, ttl_seconds, json.dumps(value))
def get(self, key: str):
full_key = self.prefix + key
raw = self.redis.get(full_key)
return json.loads(raw) if raw else None
def append_to_list(self, key: str, item):
full_key = self.prefix + key
self.redis.rpush(full_key, json.dumps(item))
def get_list(self, key: str):
full_key = self.prefix + key
return [json.loads(x) for x in self.redis.lrange(full_key, 0, -1)]
memory = RedisSharedMemory(prefix='research_project:')تحديد مساحات أسماء الذاكرة المشتركة
في الأنظمة متعددة الوكلاء المعقدة، يكتب الوكلاء أنواعًا مختلفة كثيرة من البيانات، وسرعان ما تصبح مساحة أسماء المفاتيح المسطحة فوضوية. استخدموا تحديد مساحات الأسماء الهرمي لتنظيم الذاكرة المشتركة بوضوح. ومن الأنماط الشائعة project_id:agent_role:data_type، مثل proj_123:researcher:findings أو proj_123:coder:error_log. ويسهّل ذلك الاستعلام عن جميع بيانات مشروع ما أو جميع مخرجات وكيل محدد.
class NamespacedMemory:
def __init__(self, project_id: str, agent_id: str, redis_client):
self.base = f'{project_id}:{agent_id}'
self.redis = redis_client
def write_finding(self, topic: str, content: str):
key = f'{self.base}:findings:{topic}'
self.redis.set(key, content)
def read_all_findings(self, project_id: str):
# Read findings from ALL agents in this project
pattern = f'{project_id}:*:findings:*'
keys = self.redis.keys(pattern)
return {k: self.redis.get(k) for k in keys}
# Usage
researcher_memory = NamespacedMemory('proj_123', 'researcher', redis_client)
researcher_memory.write_finding('competitors', 'OpenAI, Anthropic, Google...')
writer_memory = NamespacedMemory('proj_123', 'writer', redis_client)
all_findings = writer_memory.read_all_findings('proj_123')الذاكرة المنظمة مقابل غير المنظمة
يمكن أن يكون محتوى الذاكرة المشتركة غير منظم (كتل نصية خام سيقرأها LLM) أو منظمًا (كائنات JSON/Python ذات حقول منمّطة). وتُفضّل الذاكرة المنظمة لأنها تتيح الاستعلامات البرمجية والتحقق والدمج. احرصوا دائمًا على تعريف مخطط لما يكتبه كل وكيل في الذاكرة المشتركة، وتوثيقه، والتحقق من عمليات الكتابة وفقًا له، لمنع وكيل واحد معيب من إفساد مخزن الذاكرة.
from pydantic import BaseModel
from typing import Optional, list
from datetime import datetime
class ResearchFinding(BaseModel):
topic: str
summary: str
sources: list[str]
confidence: float # 0.0 to 1.0
written_by: str
timestamp: datetime
# Validated write - bad data is caught before it enters shared memory
def write_finding(memory, finding_dict: dict):
finding = ResearchFinding(**finding_dict) # validates on creation
memory.set(f'findings:{finding.topic}', finding.model_dump())
print(f'Validated finding written for topic: {finding.topic}')التواصل القائم على الأحداث باستخدام Pub/Sub
بدلًا من الاستقصاء الدوري للذاكرة المشتركة للتحقق من وجود تحديثات، يمكن للوكلاء استخدام تواصل النشر/الاشتراك (pub/sub) لإخطار الوكلاء الآخرين عند إكمال مهمة. ينشر الوكيل A حدثًا (مثل 'research_complete')، ثم يستيقظ الوكيل B المشترك في هذا الحدث ويبدأ المعالجة. ويدعم Redis pub/sub وقوائم الرسائل مثل RabbitMQ أو Kafka هذا النمط.
import redis
# Publisher (researcher agent)
def researcher_agent(topic, redis_client):
findings = do_research(topic)
redis_client.set(f'findings:{topic}', findings)
# Notify all subscribers that research is done
redis_client.publish('agent_events', f'research_complete:{topic}')
print(f'Research complete, published event for topic: {topic}')
# Subscriber (writer agent) - runs in separate process
def writer_agent_listener(redis_client):
pubsub = redis_client.pubsub()
pubsub.subscribe('agent_events')
for message in pubsub.listen():
if message['type'] == 'message':
event = message['data']
if event.startswith('research_complete:'):
topic = event.split(':')[1]
findings = redis_client.get(f'findings:{topic}')
write_draft(findings) # start writing immediatelyالذاكرة المشتركة في LangGraph
في LangGraph، تكون الذاكرة المشتركة بين الوكلاء هي كائن حالة الرسم البياني نفسه. إذ تقرأ كل عقدة من قاموس الحالة الموحّد ذي النوع المحدد وتكتب فيه. ويتولى LangGraph تنسيق عمليات القراءة والكتابة تلقائيًا. وفي السيناريوهات الأكثر تعقيدًا، يمكنكم أيضًا حقن عميل ذاكرة خارجي (Redis أو قاعدة بيانات) في دالة كل عقدة باستخدام حقن التبعيات.
from langgraph.graph import StateGraph
from typing import TypedDict
class SharedState(TypedDict):
# All shared data lives here - every node can read any field
query: str
research_findings: str # written by researcher, read by writer
written_draft: str # written by writer, read by reviewer
review_notes: str # written by reviewer, read by writer (loop)
final_output: str # written by synthesizer
# Researcher writes to 'research_findings'
def researcher(state: SharedState) -> dict:
findings = search_and_summarize(state['query'])
return {'research_findings': findings} # partial state update
# Writer reads 'research_findings', writes 'written_draft'
def writer(state: SharedState) -> dict:
draft = write_from_findings(state['research_findings']) # reads researcher output
return {'written_draft': draft}تعارضات الذاكرة واتساقها
عندما يكتب عدة وكلاء في الذاكرة المشتركة بالتزامن، قد تحدث تعارضات في الكتابة. فقد يستبدل وكيلان عمل أحدهما بعمل الآخر، أو يقرآن بيانات قديمة بين عمليتي القراءة والكتابة. عالجوا ذلك باستخدام: القفل المتفائل (التحقق من الإصدار قبل الكتابة)، أو عمليات المقارنة والاستبدال الذرّية في Redis، أو إجراء عمليات الكتابة بالتسلسل عبر وكيل منسّق يكون الكاتب الوحيد لحقول الذاكرة المهمة.
# Optimistic locking with Redis
def safe_write(redis_client, key, new_value, expected_version):
with redis_client.pipeline() as pipe:
try:
pipe.watch(key + ':version') # watch for concurrent modification
current_version = int(pipe.get(key + ':version') or 0)
if current_version != expected_version:
raise ValueError(f'Version conflict: expected {expected_version}, got {current_version}')
pipe.multi() # start transaction
pipe.set(key, new_value)
pipe.set(key + ':version', current_version + 1)
pipe.execute() # atomic commit
print('Write successful')
except redis.WatchError:
print('Conflict detected, retry write')مدة بقاء الذاكرة وتنظيفها
تتراكم ذاكرة الوكلاء المشتركة بمرور الوقت، وقد تنمو بلا حدود إذا لم تتم إدارتها. احرصوا دائمًا على تعيين مدة البقاء (TTL) لإدخالات الذاكرة حتى تنتهي صلاحيتها تلقائيًا. وبالنسبة إلى الذاكرة الخاصة بالمشروع، نظّفوا جميع الإدخالات عند اكتمال المشروع. استخدموا قيم TTL التي تتوافق مع المدة المتوقعة لسير العمل: مدد قصيرة (بالدقائق) للبيانات المؤقتة، ومدد أطول (من ساعات إلى أيام) للنتائج التي قد يُعاد استخدامها.
def cleanup_project_memory(redis_client, project_id: str):
pattern = f'{project_id}:*'
keys = redis_client.keys(pattern)
if keys:
redis_client.delete(*keys)
print(f'Cleaned up {len(keys)} memory entries for project {project_id}')
# Set TTL when writing
def write_with_ttl(redis_client, key, value, ttl_hours=2):
redis_client.setex(
key,
ttl_hours * 3600, # convert to seconds
json.dumps(value)
)
# Register cleanup callback when workflow completes
def on_workflow_complete(project_id):
cleanup_project_memory(redis_client, project_id)
print(f'Workflow {project_id} complete, memory cleaned up')الذاكرة كسجل لتاريخ الوكيل
يمكن للذاكرة المشتركة أيضًا تخزين سجل إجراءات الوكلاء، وليس مخرجاتهم فقط. إن تسجيل الوكيل الذي نفّذ كل إجراء، ووقت تنفيذه، وسببه، ينشئ سجل تدقيق بالغ القيمة لتصحيح الأخطاء، وفهم كيفية إنتاج المخرج النهائي، واستئناف أساليب سير العمل المتوقفة. ويمثّل سجل الإجراءات هذا النظير القائم على الذاكرة للتتبّع في LangSmith.
import time
from dataclasses import dataclass
@dataclass
class AgentAction:
agent_id: str
action_type: str # 'research', 'write', 'review', 'tool_call'
input_summary: str
output_summary: str
timestamp: float
success: bool
def log_action(memory, action: AgentAction):
key = f'action_log:{action.agent_id}:{action.timestamp}'
memory.set(key, vars(action))
# Usage in an agent
def researcher_with_logging(state, memory):
start = time.time()
findings = do_research(state['query'])
log_action(memory, AgentAction(
agent_id='researcher',
action_type='research',
input_summary=state['query'][:100],
output_summary=findings[:100],
timestamp=start,
success=True
))
return findingsاختيار بنية الذاكرة
تعتمد بنية الذاكرة المشتركة المناسبة على نموذج النشر لديكم. بالنسبة إلى أساليب سير العمل في LangGraph ضمن عملية واحدة، تكون حالة الرسم البياني كافية. أما بالنسبة إلى الوكلاء متعددي العمليات أو الموزعين، فاستخدموا Redis. وبالنسبة إلى المشروعات طويلة الأمد التي تحتاج إلى الاستمرارية عبر عمليات إعادة التشغيل، فاستخدموا قاعدة بيانات علائقية مع فهرسة مناسبة. ولتنسيق الأحداث بين الخدمات، أضيفوا آلية pub/sub فوق أي وحدة تخزين تختارونها.
تحقق سريع
اختبروا مدى فهمكم للذاكرة المشتركة والتواصل بين الوكلاء من خلال هذا الدرس.
مراجعة الدرس
تعلمتم في هذا الدرس أن نموذج السبورة يستخدم مخزن بيانات مشتركًا يمكن لأي وكيل القراءة منه والكتابة فيه، مما يتيح التنسيق الضمني دون اقتران مباشر بين الوكلاء، وأن Redis هو الخيار المفضل للذاكرة المشتركة الدائمة في أنظمة الوكلاء المتعددين الموزعة، وأن pub/sub يتيح التواصل القائم على الأحداث بحيث يستجيب الوكلاء فور اكتمال التبعيات. بعد ذلك، سنستكشف حلقة تنفيذ التعليمات البرمجية لمهام الوكلاء.
الأسئلة الشائعة
هل درس «الذاكرة المشتركة والتواصل بين الوكلاء» مجاني؟
نعم — نص درس «الذاكرة المشتركة والتواصل بين الوكلاء» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Engineering Academy، انتقل إلى CoddyKit PRO. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.
ماذا ستتعلم في «الذاكرة المشتركة والتواصل بين الوكلاء»؟
نفذوا طبقة ذاكرة مشتركة باستخدام مخزن مفتاح-قيمة يقرأ منه الوكلاء ويكتبون إليه، مما يتيح التعاون غير المتزامن دون اقتران وثيق بين الوكلاء. تتمرن على AI Engineering Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ AI Engineering Academy؟
لا تُشترط خبرة سابقة. AI Engineering Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «الذاكرة المشتركة والتواصل بين الوكلاء»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس AI Engineering Academy هذا؟
نعم. كل درس في AI Engineering Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- لماذا تصل الوكلاء المنفردة إلى طريق مسدود
- نمط المنسّق والوكيل الفرعي
- بناء مسارات متعددة الوكلاء باستخدام LangGraph
- الذاكرة المشتركة والتواصل بين الوكلاء