1. مقدمة

مشكلة "السمكة الذهبية"
تخيَّل أنّك وظّفت وكيل سفر للتخطيط لعطلة أحلامك في طوكيو. استخدِم وكيل الجلسة للاطّلاع على "مشكلة السمكة الذهبية" أثناء حدوثها.
تدخل إلى مكتبه وتقول:
"مرحبًا، أريد التخطيط لرحلة إلى طوكيو مدتها يومان. أنا مهتم بالمواقع التاريخية والسوشي".
يردّ الوكيل بحماس:
"رائع! خطّطتُ لزيارة القصر الإمبراطوري وتناول عشاء سوشي في مطعم "سوكياباشي جيرو".
تبتسم وتقول:
"يبدو ذلك رائعًا! هل يمكنك إرسال برنامج الرحلة إليّ؟"
ينظر إليك الموظف بدهشة ويسألك:
"مرحبًا، كيف يمكنني مساعدتك في التخطيط لرحلة اليوم؟"
هذه هي "مشكلة السمكة الذهبية". بدون الذاكرة، يكون كل تفاعل بمثابة صفحة بيضاء. الذكاء متوفّر، فالوكيل يعرف كيفية التخطيط للرحلات، ولكن الاستمرارية مفقودة. لكي يكون وكيل الذكاء الاصطناعي مفيدًا حقًا، يجب أن يتذكّر المعلومات.
مهمتك اليوم
في ورشة العمل هذه، ستتعرّف على كيفية حلّ مشكلة الذاكرة القصيرة المدى من خلال إنشاء وكيل سفر يتذكّر ويتعلّم ويتكيّف. ستتقدّم عبر 6 مستويات من ذاكرة الوكيل، ما يؤدي إلى إنشاء نظام يتصرّف بشكل أقل مثل روبوت دردشة وأكثر مثل مساعد شخصي مخصّص.
المستوى | الفكرة | "القوة الخارقة" |
المستوى 1 | الجلسة والحالة | إجراء محادثة بدون نسيان |
المستوى 2 | حالة الوكلاء المتعددين | مشاركة الملاحظات بين أعضاء الفريق |
المستوى 3 | Persistence | تذكُّر معلوماتك حتى بعد إعادة تشغيل النظام |
المستوى 4 | عمليات إعادة الاتصال | جارٍ تعديل الذاكرة بشكل مستقل تمامًا |
المستوى 5 | الأدوات المخصّصة | قراءة ملفات المستخدمين الشخصية المنظَّمة وكتابتها |
المستوى 6 | الذاكرة المتعددة الوسائط | "رؤية" الصور والفيديوهات وتذكّرها |
مجموعة الذاكرة في ADK
قبل كتابة الرمز البرمجي، لنتعرّف على الأدوات التي نستخدمها. توفّر حزمة تطوير الوكلاء (ADK) من Google طريقة منظَّمة للتعامل مع الذاكرة:
- الجلسة: حاوية المحادثة. ويحتفظ بسجلّ ما قيل.
- الحالة: "مفكرة" تتضمّن أزواج مفاتيح وقيم مرتبطة بالجلسة. يستخدم الوكلاء هذه الذاكرة لتخزين حقائق محدّدة (مثل
destination="Tokyo"). - MemoryService: مساحة تخزين طويلة الأمد هذا هو المكان الذي نحتفظ فيه بالبيانات إلى الأبد، مثل الإعدادات المفضّلة للمستخدم أو المستندات التي تم تحليلها.
2. إعداد
لتشغيل وكلاء الذكاء الاصطناعي، نحتاج إلى عنصرَين: مشروع على Google Cloud لتوفير الأساس.
الجزء الأول: تفعيل حساب الفوترة
لتنفيذ هذا الدرس التطبيقي حول الترميز، تحتاج إلى حساب فوترة يتضمّن بعض الرصيد. استخدِم الأرصدة من البانر في أعلى هذا الدرس التطبيقي حول الترميز للبدء. إذا كنت مرتبطًا بحساب فوترة، يمكنك تخطّي هذه الخطوة.
الجزء الثاني: البيئة المفتوحة
- 👉 انقر على هذا الرابط للانتقال مباشرةً إلى محرِّر Cloud Shell
- 👉 إذا طُلب منك منح الإذن في أي وقت اليوم، انقر على منح الإذن للمتابعة.

- 👉 إذا لم يظهر Terminal في أسفل الشاشة، افتحه:
- انقر على عرض
- انقر على Terminal

- 👉💻 في الوحدة الطرفية، تأكَّد من أنّه تمّت مصادقتك وأنّ المشروع مضبوط على رقم تعريف مشروعك باستخدام الأمر التالي:
gcloud auth list - 👉💻 استنسِخ مشروع bootstrap من GitHub:
git clone https://github.com/cuppibla/memory_agent_starter - 👉💻 شغِّل نص التهيئة البرمجي من دليل المشروع.
سيتولّى النص البرمجي بقية عملية الإعداد تلقائيًا.cd ~/memory_agent_starter ./init.sh - 👉💻 اضبط رقم تعريف المشروع المطلوب:
gcloud config set project $(cat ~/project_id.txt) --quiet
الجزء الثالث: إعداد الإذن
- 👉💻 فعِّل واجهات برمجة التطبيقات المطلوبة باستخدام الأمر التالي. قد تستغرق العملية بضع دقائق.
gcloud services enable \ cloudresourcemanager.googleapis.com \ servicenetworking.googleapis.com \ run.googleapis.com \ aiplatform.googleapis.com \ compute.googleapis.com - 👉💻 امنح الأذونات اللازمة من خلال تنفيذ الأوامر التالية في الوحدة الطرفية:
. ~/memory_agent_starter/set_env.sh
لاحظ أنّه تم إنشاء ملف .env لك. يعرض معلومات مشروعك.
3- الأساسيات: الجلسة والحالة

المفهوم: السياق هو الأساس
أبسط شكل من أشكال الذاكرة هو ذاكرة الجلسة. وهذا ما يسمح للوكيل بمعرفة أنّ كلمة "هذا" في جملة "أريد شراء هذا" تشير إلى الحذاء الذي كنت تتحدّث عنه قبل 10 ثوانٍ.
في ADK، ندير ذلك باستخدام العنصر Session.
- النهج غير المعتمد على الحالة: إنشاء جلسة جديدة لكل رسالة
- النهج المستند إلى الحالة: إنشاء جلسة واحدة وإعادة استخدامها للمحادثة بأكملها.
الخطوة 1: فحص الوكيل
👉💻 في وحدة Cloud Shell الطرفية، افتح الملف في محرِّر Cloud Shell من خلال تشغيل:
cloudshell edit ~/memory_agent_starter/01_session_agent/agent.py
فتح "~/memory_agent_starter/01_session_agent/agent.py"
👉 ابحث عن التعليق # TODO: Create a root agent داخل الدالة agent.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
root_agent = LlmAgent(
name="multi_day_trip_agent",
model="gemini-2.5-flash",
description="Agent that progressively plans a multi-day trip, remembering previous days and adapting to user feedback.",
instruction="""
You are the "Adaptive Trip Planner" 🗺️ - an AI assistant that builds multi-day travel itineraries step-by-step.
Your Defining Feature:
You have short-term memory. You MUST refer back to our conversation to understand the trip's context, what has already been planned, and the user's preferences. If the user asks for a change, you must adapt the plan while keeping the unchanged parts consistent.
Your Mission:
1. **Initiate**: Start by asking for the destination, trip duration, and interests.
2. **Plan Progressively**: Plan ONLY ONE DAY at a time. After presenting a plan, ask for confirmation.
3. **Handle Feedback**: If a user dislikes a suggestion (e.g., "I don't like museums"), acknowledge their feedback, and provide a *new, alternative* suggestion for that time slot that still fits the overall theme.
4. **Maintain Context**: For each new day, ensure the activities are unique and build logically on the previous days. Do not suggest the same things repeatedly.
5. **Final Output**: Return each day's itinerary in MARKDOWN format.
""",
tools=[google_search]
)
تطلب التعليمات من نموذج اللغة الكبير أن يتذكّر، ولكن يجب أن يوفّر الرمز البرمجي إمكانية التذكّر.
الخطوة 2: السيناريوهان
فتح "~/memory_agent_starter/01_session_agent/main.py"
👉 في وحدة Cloud Shell الطرفية، افتح الملف في محرِّر Cloud Shell من خلال تشغيل:
cloudshell edit ~/memory_agent_starter/01_session_agent/main.py
افتح ~/memory_agent_starter/01_session_agent/main.py، وابحث عن التعليق # TODO: Create a runner with in memorysession service داخل الدالة main.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
runner = Runner(
agent=agent,
session_service=session_service,
app_name=agent.name
)
👉 ابحث عن التعليق # TODO: create a different session to test داخل الدالة main.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
tokyo_session_2 = await session_service.create_session(
app_name=multi_day_agent.name,
user_id=user_id
)
اختبارها
لدينا دالتان توضّحان الفرق بين ذاكرة "السمكة الذهبية" وذاكرة "الفيل".
السيناريو 1: Stateful (جلسة مشتركة)
async def run_trip_same_session_scenario(session_service, user_id):
# 1. Create ONE session
trip_session = await session_service.create_session(...)
# 2. Turn 1
await run_agent_query(..., trip_session, ...)
# 3. Turn 2 - REUSING the same session!
# The agent can "see" Turn 1 because it's in the session history.
await run_agent_query(..., trip_session, ...)
السيناريو الثاني: بدون حالة (جلسة جديدة في كل مرة)
async def run_trip_different_session_scenario(session_service, user_id):
# Turn 1
tokyo_session = await session_service.create_session(...)
await run_agent_query(..., tokyo_session, ...)
# Turn 2 - Creating a FREASH session
# The agent has NO IDEA what happened in Turn 1.
tokyo_session_2 = await session_service.create_session(...)
await run_agent_query(..., tokyo_session_2, ...)
الخطوة 3: تشغيل الوكيل
لنطّلِع على الفرق بينهما عمليًا. شغِّل النص البرمجي:
👉💻 في سطر الأوامر، نفِّذ الأمر التالي:
cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/01_session_agent/main.py
السيناريو 1: يتذكّر الوكيل خياراتك المفضّلة من الرسالة الأولى ويعدّل الخطة في الرسالة الثانية.
السيناريو 2: في الجولة الثانية ("هل تتذكر ما أعجبني في الطعام؟")، يفشل الوكيل تمامًا لأنّها جلسة جديدة. وهي تعني فعليًا: "لا أعرف ما الذي تتحدث عنه".
الخلاصة الرئيسية
القاعدة الأولى في "الذاكرة": يجب دائمًا إعادة استخدام session.id للحفاظ على سياق المحادثة. الكائن Session هو مخزن الذاكرة المؤقتة القصيرة المدى للوكيل.
4. الفريق - حالة الوكلاء المتعدّدين

المفهوم: "لعبة الهاتف"
عندما يعمل عدة وكلاء معًا، يكونون مثل زملاء يتبادلون مجلد ملفات ذهابًا وإيابًا. إذا كتب أحد الوكلاء ملاحظة في المجلد، يجب أن يتمكّن الوكيل التالي من قراءتها.
في ADK، هذا "المجلد" هو الولاية.
- الحالة هي قاموس (
{"key": "value"}) يتوفّر داخل الجلسة. - ويمكن لأي وكيل في الجلسة القراءة منه أو الكتابة فيه.
الخطوة 1: فحص سير العمل
👉💻 في وحدة Cloud Shell الطرفية، افتح الملف في محرِّر Cloud Shell من خلال تشغيل:
cloudshell edit ~/memory_agent_starter/02_multi_agent/agent.py
👉في الملف ~/memory_agent_starter/02_multi_agent/agent.py، ابحث عن التعليق # TODO: foodie agent.
استبدِل هذا السطر بالكامل بالرمز التالي:
foodie_agent = LlmAgent(
name="foodie_agent",
model="gemini-2.5-flash",
tools=[google_search],
instruction="""You are an expert food critic. Your goal is to find the best restaurant based on a user's request.
When you recommend a place, you must output *only* the name of the establishment and nothing else.
For example, if the best sushi is at 'Jin Sho', you should output only: Jin Sho
""",
output_key="destination" # ADK will save the agent's final response to state['destination']
)
👉 ابحث عن التعليق # TODO: transportation agent داخل الدالة agent.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
transportation_agent = LlmAgent(
name="transportation_agent",
model="gemini-2.5-flash",
tools=[google_search],
instruction="""You are a navigation assistant. Given a destination, provide clear directions.
The user wants to go to: {destination}.
Analyze the user's full original query to find their starting point.
Then, provide clear directions from that starting point to {destination}.
""",
)
👉 ابحث عن التعليق # TODO: root_agent داخل الدالة agent.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
root_agent = SequentialAgent(
name="find_and_navigate_agent",
sub_agents=[foodie_agent, transportation_agent],
description="A workflow that first finds a location and then provides directions to it."
)
لدينا الآن وكيلان يعملان بالتسلسل:
- وكيل Foodie: يعثر على مطعم.
- وكيل النقل: يقدّم الاتجاهات إلى ذلك المطعم.
The Magic Hand-off: Notice how foodie_agent passes the baton to transportation_agent.
foodie_agent = LlmAgent(
# ...
# CRITICAL: This tells ADK to save the agent's output to state['destination']
output_key="destination"
)
transportation_agent = LlmAgent(
# ...
# CRITICAL: This injects state['destination'] into the prompt
instruction="""
The user wants to go to: {destination}.
Provide clear directions...
""",
)
-
output_key="destination": يتم حفظ إجابة Foodie Agent بكفاءة. -
{destination}: يقرأ وكيل النقل هذه الإجابة تلقائيًا.
(لا مطلوب اتخاذ إجراء) الخطوة 2: Orchestrator
فتح "02_multi_agent/main.py"
نستخدم SequentialAgent لتشغيلها بالترتيب.
# 1. Create a single session for the sequential agent
session = await session_service.create_session(...)
# 2. Run the query
# The SequentialAgent manages the state flow:
# Query -> Foodie -> state['destination'] -> Transportation -> Final Answer
await run_agent_query(root_agent, query, ...)
يرسل المستخدم طلبًا واحدًا:
"Find best sushi in Palo Alto and then tell me how to get there."
يعمل الوكلاء معًا للإجابة عنه.
الخطوة 3: إدارة الفريق
👉💻 في Cloud Shell Terminal، نفِّذ سير العمل المتعدّد الوكلاء:
cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/02_multi_agent/main.py
ماذا يحدث؟
- وكيل محبّي الطعام: يعثر على مطعم "Jin Sho" (أو مطعم مشابه).
- ADK: يحفظ "جين شو" في
state['destination']. - وكيل النقل: يتلقّى "جين شو" في تعليماته.
- النتيجة: "للوصول إلى مطعم جين شو من محطة كالترين، يمكنك السير في شارع الجامعة..."
الخلاصة الرئيسية
القاعدة رقم 2 للذاكرة: استخدِم الحالة لتمرير المعلومات المنظَّمة بين الوكلاء. استخدِم output_key للكتابة و{placeholders} للقراءة.
5- The Reboot - Persistence

المفهوم: "مشكلة إعادة التشغيل"
حتى الآن، كانت ذاكرتنا InMemory. إذا أوقفت النص البرمجي وبدأت تشغيله مرة أخرى، سينسى الوكيل كل شيء. يشبه ذلك جهاز كمبيوتر يمسح محرك الأقراص الثابتة في كل مرة يتم إيقافه فيها.
لحلّ هذه المشكلة، نحتاج إلى المثابرة. نستبدل InMemorySessionService بـ DatabaseSessionService.
الخطوة 1: تبديل قاعدة البيانات
👉💻 في وحدة Cloud Shell الطرفية، افتح الملف في محرِّر Cloud Shell من خلال تشغيل:
cloudshell edit ~/memory_agent_starter/03_persistent_agent/main.py
👉 في الملف ~/memory_agent_starter/03_persistent_agent/main.py، ابحث عن التعليق # TODO: Configuration for Persistent Sessions.
استبدِل هذا السطر بالكامل بالرمز التالي:
SESSIONS_DIR = Path(os.path.expanduser("~")) / ".adk_codelab" / "sessions"
os.makedirs(SESSIONS_DIR, exist_ok=True)
SESSION_DB_FILE = SESSIONS_DIR / "trip_planner.db"
SESSION_URL = f"sqlite:///{SESSION_DB_FILE}"
الآن، يتم حفظ كل جلسة وحدث في ملف SQLite.
الخطوة 2: استرداد البيانات من جلسات متعددة
لا تتيح ميزة "الاستمرارية" استئناف محادثة فحسب، بل تتيح أيضًا التعلّم من المحادثات السابقة.
في الملف نفسه ~/memory_agent_starter/03_persistent_agent/main.py، انتقِل إلى إطار الاختبار 3: استرداد البيانات من جلسات مختلفة.
👉 العثور على التعليق # TODO: retrieve the previous session manually
استبدِل هذا السطر بالكامل بالرمز التالي:
old_session = await session_service.get_session(
app_name=root_agent.name, user_id="user_01", session_id=session_id
)
👉 ابحث عن التعليق # TODO: Extract content from the OLD session داخل الدالة main.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
previous_context += f"- {role}: {text}\n"
👉 ابحث عن التعليق # TODO: Manually inject the context to the query داخل الدالة main.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
query_3 = f"""
{previous_context}
I'm planning a new trip to Osaka this time.
Based on my previous preferences (above), what should I eat?
"""
يحاكي هذا الإجراء عودة المستخدم بعد أشهر. يمكنك استرداد هذا السجلّ القديم فقط باستخدام قاعدة بيانات.
الخطوة 3: تجاوز عملية إعادة التشغيل
👉💻 في الوحدة الطرفية، شغِّل النص البرمجي:
cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/03_persistent_agent/main.py
يتم إنشاء ملف باسم ~/memory_agent_starter/trip_planner.db. جرِّب ما يلي: شغِّل النص البرمجي مرتين.
- في المرة الثانية، ابحث عن "تم استئناف الجلسة الحالية".
- سيتذكّر الوكيل سياق الحديث من عملية التشغيل الأولى لأنّه يتم تحميله من ملف قاعدة البيانات.
الخلاصة الرئيسية
القاعدة رقم 3 في "الذاكرة": استخدِم DatabaseSessionService في الإنتاج. ويضمن ذلك بقاء محادثات المستخدمين بعد إعادة تشغيل الخادم ويتيح تحليل السجلّ على المدى الطويل.
6. The Spy - Callbacks

في بعض الأحيان، قد تحتاج إلى تعديل الذاكرة تلقائيًا استنادًا إلى ما يفعله الوكيل، وليس فقط ما يقوله. أنت بحاجة إلى "جاسوس" يراقب الوكيل ويدوّن الملاحظات.
في ADK، هذا الجاسوس هو Callback. 
-
after_tool_callback: دالة يتم تشغيلها في كل مرة يعمل فيها الوكيل. -
ToolContext: طريقة للكتابة في الحالة من داخل هذه الدالة.
الخطوة 1: المنطق
👉💻 في وحدة Cloud Shell الطرفية، افتح الملف في محرِّر Cloud Shell من خلال تشغيل:
cloudshell edit ~/memory_agent_starter/04_stateful_agent/agent.py
👉 في الملف ~/memory_agent_starter/04_stateful_agent/agent.py، ابحث عن التعليق # TODO: Implement call back logic
استبدِل هذا السطر بالكامل بالرمز التالي:
def save_activity_type_callback(
tool,
args: Dict[str, Any],
tool_context: ToolContext,
tool_response: Dict[str, Any],
) -> Optional[Dict[str, Any]]:
"""
Callback to save the TYPE of activity just planned into the session state.
"""
# 1. Get the actual agent name.
if tool.name == "transfer_to_agent":
agent_name = args.get("agent_name")
else:
agent_name = tool.name
activity_type = "unknown"
# 2. Determine the type based on which agent was actually used
if agent_name == "museum_expert":
activity_type = "CULTURAL"
elif agent_name == "restaurant_expert":
activity_type = "FOOD"
elif agent_name == "outdoor_expert":
activity_type = "OUTDOOR"
print(f"\n🔔 [CALLBACK] The planner transferred to '{agent_name}'.")
# 3. Update the state directly
tool_context.state["last_activity_type"] = activity_type
print(f"💾 [STATE UPDATE] 'last_activity_type' is now set to: {activity_type}\n")
return tool_response
👉 في الملف نفسه، ابحث عن التعليق # TODO: add callback to root agent داخل الدالة 04_stateful_agent/agent.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
after_tool_callback=save_activity_type_callback,
التعليمات الديناميكية: أصبحت تعليمات الوكيل الآن دالة وليست سلسلة. يتغيّر حسب الحالة.
def get_planner_instruction(context):
last_activity = context.state.get("last_activity_type", "None")
return f"""
The last activity was: {last_activity}
If last_activity is 'CULTURAL' -> `museum_expert` is BANNED.
"""
الخطوة 3: اختبار الجاسوس
👉💻 في الوحدة الطرفية، شغِّل النص البرمجي من خلال نسخ الأمر أدناه ولصقه:
cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/04_stateful_agent/main.py
عند تشغيل هذا الوكيل، ستظهر لك حلقة تكرار.
- الجولة 1: تطلب متحفًا. مجموعات تجسّسية من
last_activity="CULTURAL" - الردّ الثاني: تطلب متحفًا آخر.
- تعديلات على تعليمات الوكيل: "المحتوى الثقافي محظور".
- يقول موظف الدعم: "لا يمكنني زيارة متحف آخر. ما رأيك في حديقة؟"
اطّلِع على سجلّات وحدة التحكّم بحثًا عن [CALLBACK] و[STATE UPDATE]. يمكنك الاطّلاع على الذاكرة وهي تتغيّر في الوقت الفعلي أثناء عمل الوكيل.
الخلاصة الرئيسية
القاعدة الرابعة للذاكرة: استخدِم عمليات الاستدعاء لإدارة الحالة بشكل آلي. ينشئ الوكيل سياقه الخاص من خلال تنفيذ مهامه.
7. The File Cabinet - Custom Tools
المفهوم: "الذاكرة المنظَّمة"

حتى الآن، كانت ميزة "الذاكرة" عبارة عن سجلّ محادثات أو زوج بسيط من المفتاح والقيمة. ولكن ماذا لو كنت بحاجة إلى تذكُّر ملف مستخدم شخصي معقّد، مثل diet: vegan, budget: high, pets: [cat, dog]؟
لهذا الغرض، نتعامل مع الذاكرة على أنّها أداة. يقرّر الوكيل صراحةً متى يفتح خزانة الملفات (القراءة) ومتى يقدّم تقريرًا (الكتابة). 
الخطوة 1: الأدوات
👉💻 في وحدة Cloud Shell الطرفية، افتح الملف في محرِّر Cloud Shell من خلال تشغيل:
cloudshell edit ~/memory_agent_starter/05_profile_agent/tools.py
👉 في هذا الملف: ~/memory_agent_starter/05_profile_agent/tools.py.
نحتاج إلى تنفيذ هاتين الأداتين المحدّدتين:
save_user_preferences: يكتب في قاعدة بيانات.recall_user_preferences: يقرأ من قاعدة بيانات.
ابحث عن التعليق # TODO: implement save_user_preferences tools داخل الدالة ~/memory_agent_starter/05_profile_agent/tools.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
def save_user_preferences(tool_context: ToolContext, new_preferences: Dict[str, Any]) -> str:
user_id = tool_context.session.user_id
with sqlite3.connect(USER_DB_FILE) as conn:
for key, value in new_preferences.items():
conn.execute("INSERT INTO user_preferences (user_id, pref_key, pref_value) VALUES (?, ?, ?) ON CONFLICT(user_id, pref_key) DO UPDATE SET pref_value = excluded.pref_value;",
(user_id, key, json.dumps(value)))
return f"Preferences updated: {list(new_preferences.keys())}"
👉 ابحث عن التعليق # TODO: implement recall_user_preferences tools داخل الدالة 05/tools.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
def recall_user_preferences(tool_context: ToolContext) -> Dict[str, Any]:
user_id = tool_context.session.user_id
preferences = {}
with sqlite3.connect(USER_DB_FILE) as conn:
rows = conn.execute("SELECT pref_key, pref_value FROM user_preferences WHERE user_id = ?", (user_id,)).fetchall()
if not rows: return {"message": "No preferences found."}
for key, value_str in rows: preferences[key] = json.loads(value_str)
return preferences
تفرض التعليمات سير عمل معيّنًا:
instruction="""
1. RECALL FIRST: First action MUST be `recall_user_preferences`.
3. LEARN: If a user states a new preference, use `save_user_preferences`.
"""
الخطوة 2: التنفيذ
👉💻 في وحدة Cloud Shell الطرفية، افتح الملف في محرِّر Cloud Shell من خلال تشغيل:
cloudshell edit ~/memory_agent_starter/05_profile_agent/main.py
فتح "~/memory_agent_starter/05_profile_agent/main.py"
على عكس الوحدات السابقة التي كانت فيها حزمة ADK تتعامل مع الحالة تلقائيًا، يتحكّم الوكيل هنا في الحالة.
- يختار الاتصال بـ "
recall_user_preferences" في البداية. - يختار الاتصال بـ "
save_user_preferences" عندما تقول "أنا نباتي".
الخطوة 3: إنشاء الملف الشخصي
👉💻 تشغيل النص البرمجي:
cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/05_profile_agent/main.py
جرِّب مسار المحادثة التالي:
- "مرحبًا، أريد التخطيط لعشاء". -> يتحقّق الوكيل من قاعدة البيانات، ولا يعثر على أي شيء. يطلب منك تحديد خياراتك المفضّلة.
- "أنا نباتي". -> يحفظ الوكيل "نباتي" في قاعدة البيانات.
- أعِد تشغيل النص البرمجي.
- "مرحبًا، أريد التخطيط لعشاء". -> يتحقّق الوكيل من قاعدة البيانات، ويرى "نباتي"، ويقترح مطعمًا نباتيًا على الفور.
الخلاصة الرئيسية
القاعدة الخامسة للذاكرة: بالنسبة إلى البيانات المعقّدة والمنظَّمة، يجب تزويد الوكيل بأدوات القراءة والكتابة. السماح للنموذج اللغوي الكبير بإدارة مساحة التخزين الطويلة المدى الخاصة به
8. The Brain - Multimodal Memory

المفهوم: "التجربة الإنسانية"
يتذكّر البشر أكثر من مجرد نص. نتذكّر أجواء الصورة ونبرة الصوت ومشاعر الفيديو.
يتيح Vertex AI Memory Bank لوكيلك التعامل مع الذاكرة المتعددة الوسائط. يمكنه استيعاب الصور والفيديوهات والمحتوى الصوتي و"فهمها" واستردادها لاحقًا.
الخطوة 1: الإعداد
👉💻 في وحدة Cloud Shell الطرفية، افتح الملف في محرِّر Cloud Shell من خلال تشغيل:
cloudshell edit ~/memory_agent_starter/06_multimodal_agent/main.py
👈 افتح 06_multimodal_agent/main.py. ابحث عن التعليق "# TODO: Configure Memory Bank Topic".
استبدِل هذا السطر بالكامل بالرمز التالي:
travel_topics = [
MemoryTopic(
managed_memory_topic=ManagedMemoryTopic(
managed_topic_enum=ManagedTopicEnum.USER_PREFERENCES
)
),
MemoryTopic(
managed_memory_topic=ManagedMemoryTopic(
managed_topic_enum=ManagedTopicEnum.USER_PERSONAL_INFO
)
),
MemoryTopic(
custom_memory_topic=CustomMemoryTopic(
label="travel_experiences",
description="""Memorable travel experiences including:
- Places visited and impressions
- Favorite restaurants, cafes, and food experiences
- Preferred accommodation types and locations
- Activities enjoyed (museums, hiking, beaches, etc.)
- Travel companions and social preferences
- Photos and videos from trips with location context""",
)
),
MemoryTopic(
custom_memory_topic=CustomMemoryTopic(
label="travel_preferences",
description="""Travel style and preferences:
- Budget preferences (luxury, mid-range, budget)
- Transportation preferences (flying, trains, driving)
- Trip duration preferences
- Season and weather preferences
- Cultural interests and language abilities
- Dietary restrictions and food preferences""",
)
),
MemoryTopic(
custom_memory_topic=CustomMemoryTopic(
label="travel_logistics",
description="""Practical travel information:
- Passport and visa information
- Frequent flyer numbers and hotel loyalty programs
- Emergency contacts
- Medical considerations and insurance
- Packing preferences and essentials
- Time zone preferences and jet lag strategies""",
)
),
]
العثور على التعليق "# TODO: Configure Memory Bank Customization"
استبدِل هذا السطر بالكامل بالرمز التالي:
memory_bank_config = {
"customization_configs": [
{
"memory_topics": travel_topics,
}
],
"similarity_search_config": {
"embedding_model": f"projects/{PROJECT_ID}/locations/{LOCATION}/publishers/google/models/gemini-embedding-001"
},
"generation_config": {
"model": f"projects/{PROJECT_ID}/locations/{LOCATION}/publishers/google/models/gemini-2.5-flash"
},
}
الخطوة 2: استيعاب العالم
في test_trip_planner، نرسل ما يلي:
- رسالة نصية ("مرحبًا")
- صورة (معلم)
- فيديو (البحر الأبيض المتوسط)
- مقطع صوتي (ملاحظة صوتية حول غايتا)
ابحث عن التعليق # TODO create session service and memory service داخل الدالة 6_multimodal_agent/main.py.
استبدِل هذا السطر بالكامل بالرمز التالي:
session_service = VertexAiSessionService(
project=PROJECT_ID, location=LOCATION, agent_engine_id=agent_engine_id
)
memory_service = VertexAiMemoryBankService(
project=PROJECT_ID, location=LOCATION, agent_engine_id=agent_engine_id
)
👉 في الملف نفسه 06_multimodal_agent/main.py، ابحث عن التعليق # TODO: create memory from session
استبدِل هذا السطر بالكامل بالرمز التالي:
await memory_service.add_session_to_memory(final_session_state)
هذا هو السطر السحري. ويتم إرسال كل هذه الوسائط التفاعلية المتقدّمة إلى Vertex AI التي تعالجها وتفهرسها.
الخطوة 3: الاسترداد
👉💻 في وحدة Cloud Shell الطرفية، افتح الملف في محرِّر Cloud Shell من خلال تشغيل:
cloudshell edit ~/memory_agent_starter/06_multimodal_agent/agent.py
يحتوي الوكيل على PreloadMemoryTool.
tools=[PreloadMemoryTool(), budget_tool]
عند بدء جلسة جديدة، تبحث هذه الأداة تلقائيًا في "بنك الذاكرة" عن تجارب سابقة ذات صلة وتُدرجها في السياق.
الخطوة 4: تشغيل Brain
👉💻 في وحدة Cloud Shell الطرفية، شغِّل النص البرمجي (ملاحظة: يتطلّب ذلك مشروعًا على Google Cloud مع تفعيل Vertex AI):
cd ~/memory_agent_starter
uv run python ~/memory_agent_starter/06_multimodal_agent/main.py
شاهِد الخطوة الأخيرة من عملية التحقّق:
"استنادًا إلى الصورة والفيديو والمقطع الصوتي التي شاركتها معك سابقًا..."
سيردّ الوكيل بما يلي:
"عليك زيارة غايتا. عرضت لي فيديو للبحر الأبيض المتوسط، ومقطعًا صوتيًا قلت فيه إنّك تحب غايتا".
فقد ربطت بين أنواع مختلفة من الوسائط من الماضي.
الخلاصة الرئيسية
القاعدة السادسة للذاكرة: استخدِم Vertex AI Memory Bank للحصول على أفضل تجربة ذاكرة. فهو يجمع النصوص والصور والفيديوهات في عقل واحد قابل للبحث.
9. الخاتمة
لقد انتقلت من مستوى "سمكة ذهبية" إلى مستوى "فيل متعدّد الوسائط".
لقد أنشأت | الإمكانية |
وكيل الجلسة | ذاكرة المحادثة القصيرة المدى |
الوكلاء المتعددون | ذاكرة الفريق المشتركة |
الوكيل الثابت | السجلّ الطويل الأمد |
الوكيل الذي يحفظ الحالة | ذاكرة ديناميكية يتم تعديلها تلقائيًا |
Profile Agent | ذاكرة البيانات المنظَّمة |
الوكيل المتعدّد الوسائط | ذاكرة حسية شبيهة بالبشر |
الثقة تُبنى على الذاكرة. من خلال تنفيذ هذه الأنماط، يمكنك إنشاء وكلاء يحترمون وقت المستخدم وسجلّه، ما يؤدي إلى تفاعلات أعمق وأكثر فعالية.
ابدأ بإنشاء وكلائك المخصّصين اليوم.