פרופילים של זיכרון

אתם יכולים להשתמש ב-Memory Bank כדי ליצור פרופילים מובנים, שהם מבני נתונים עם סכימות סטטיות שאוכלסו ועודכנו באמצעות מודלים של LLM. הגדרת סכימה קבועה מבטיחה לסוכנים גישה מיידית למידע מתפתח עם זמן אחזור נמוך, בלי שהם יצטרכו לבצע פעולות חיפוש יקרות במהלך סשן.

כדי להשלים את השלבים שמוצגים במדריך הזה, צריך קודם לבצע את השלבים במאמר הגדרה של Memory Bank.

סקירה כללית

שימוש בפרופילים מובנים עם הסוכן מבטיח שהמידע שחולץ – כמו המערכות הטכניות או ההעדפות של המשתמש – יהיה זמין במהירות, בתמציתיות ובפורמט עקבי. לדוגמה, אפשר לאחזר פרופילים שכוללים תוכן כמו:

MemoryProfile(
    profile={
        "technical_stacks": "ADK, Python",
        "preferred_language": "Python",
        "tone_preference": "Succinct"
    },
    schema_id="user-profile"
)

פרופילים מובְנים מותאמים לאחזור עם זמן אחזור נמוך, כי העבודה הקשה של אצירת המידע מתבצעת בזמן היצירה. הם מצוינים להתחלת אינטראקציה בין נציג למשתמש.

הסבר על פרופילים מובְנים של זיכרון

פרופילים מובנים נוצרים באותן שיטות (GenerateMemories ו-IngestEvents) שבהן משתמשים כדי ליצור זיכרונות בשפה טבעית. אחרי שמגדירים סכימה, Memory Bank ינסה ליצור באופן אוטומטי פרופילים שתואמים לסכימה באמצעות מקורות הנתונים שסיפקתם.

כשמשתמשים בפרופילים מובנים, Memory Bank מבצע את הפעולות הבאות במהלך יצירת הזיכרון:

  • חילוץ: חילוץ מידע והקשר שמתאימים לסכימה ממקור הנתונים. המערכת תחלץ רק מידע שתואם לסכימה. אפשר לבדוק אילו פרטים והקשר חולצו באמצעות עדכונים של הזיכרון.
  • איחוד: עדכון (אם יש צורך) של שדות קיימים בפרופיל. LLM יקבע איך לעדכן את התוכן הקיים על סמך המידע וההקשר החדשים שחולצו. אם השדה לא קיים כבר בפרופיל, המיזוג יידלג על השדה והוא יעודכן ישירות עם המידע שחולץ.

הפרופילים מבודדים על סמך scope (למשל {"user_id": "123"}) שסיפקתם כששלחתם נתונים ל-Memory Bank. לכל סכימה והיקף, Memory Bank שומר פרופיל יחיד כמקור מידע מהימן. פרופיל שנוצר מורכב ממופע אחד או יותר של Memory. כל מופע של Memory מייצג שדה יחיד בפרופיל שבו אפשר לבדוק את המטא-נתונים ואת היסטוריית גרסאות של השדה, כמו:

Memory(
     create_time=datetime.datetime(...),
     memory_type=<MemoryType.STRUCTURED_PROFILE: 'STRUCTURED_PROFILE'>,
     name='projects/.../locations/.../reasoningEngines/.../memories/...',
     scope={
       'user_id': '123'
     },
     structured_content=MemoryStructuredContent(
       data={
         'language_preference': 'Java'
       },
       schema_id='user-profile'
     ),
     update_time=datetime.datetime(...),
     expire_time=datetime.datetime(...),
     metadata={...}
)

הגדרת סכימה

הפרופילים שנוצרים ב-Memory Bank תואמים לסכימה שהוגדרה כשהמופע של Agent Platform נוצר או עודכן. אתם יכולים להשתמש במודל pydantic כדי להגדיר את השדות שאתם רוצים ש-Memory Bank יחלץ וישמור. לדוגמה:

from pydantic import BaseModel, Field
from typing import Literal

class UserProfile(BaseModel):
    name: str = Field(
      description="Name of the user.")
    technical_stack: str = Field(
      description="Comma-separated list tools or languages used by the user.")
    primary_goal: str = Field(
      description="The main objective the user is pursuing.")
    expertise_level: str = Field(
      description="Current skill level (e.g., Junior, Senior).")
    job_status: Literal['unemployed', 'part_time', 'full_time', 'student'] = Field(
      description="The job status of the individual")

מעלים את הסכימה ל-Memory Bank כשיוצרים או מעדכנים את המופע של Agent Platform. אפשר להגדיר כמה סכימות פרופילים עצמאיות. כל סכימה צריכה להיות מזוהה באמצעות מזהה ייחודי:

schema_config = {
  "id": "user-profile",
  "memory_schema": UserProfile.model_json_schema()
}

memory_bank = client.agent_engines.create(
    config={
        "context_spec": {
            "memory_bank_config": {
                "structured_memory_configs": [
                    {
                        "schema_configs": [schema_config]
                    }
                ]
            }
        }
    }
)

כברירת מחדל, Memory Bank תמיד ינסה לחלץ זיכרונות בשפה טבעית. אם אתם רוצים ש-Memory Bank ייצור רק פרופילים, אתם יכולים להשבית את תהליך יצירת הזיכרון בשפה טבעית:

memory_bank = client.agent_engines.create(
    config={
        "context_spec": {
            "memory_bank_config": {
                "structured_memory_configs": [
                    {
                        "schema_configs": [schema_config]
                    }
                ],
                # Optional: Disable natural language memories.
                "customization_configs": [
                    {"disable_natural_language_memories": True}
                ]
            }
        }
    }
)

יצירת פרופיל

כדי להפעיל את יצירת הזיכרונות המובנים, צריך לספק היסטוריית שיחות (אירועים) לשיטות GenerateMemories או IngestEvents. בדומה לזיכרונות בשפה טבעית, Memory Bank משתמש במודלים מסוג LLM כדי לחלץ מידע משמעותי ממקורות הנתונים ולצרף אותו לזיכרונות קיימים.

דוגמה

בדוגמה הבאה מוסבר איך ליצור פרופיל זיכרון באמצעות הסכימה שהוגדרה קודם.

בסדרת האירועים הראשונה שנשלחת אל Memory Bank בהקשר {"user_id": "123"}, המשתמש מציין שהוא עובד עם סוכני ADK:

client.agent_engines.memories.generate(
  name=memory_bank.api_resource.name,
  scope={"user_id": "123"},
  direct_contents_source={
    "events": [
      {"content": {
        "parts": [{
          "text": "Can you help me build an ADK agent that organizes my daily tasks?"}]}}]
  }
)

‫Memory Bank מחלץ את הערך ADK לשדה technical_stack בסכימה. שדות אחרים לא מאוכלסים כי האירוע שנקלט לא מכיל מידע רלוונטי לשאר הסכימה. מכיוון שזו האינטראקציה הראשונה בהיקף הזה, המערכת מדלגת על איחוד הנתונים ומאתחלת את הפרופיל עם הערך הראשוני הזה.

result = client.agent_engines.memories.retrieve_profiles(
    name=memory_bank.api_resource.name,
    scope={"user_id": "123"},
)

"""
Returns:

RetrieveProfilesResponse(
  profiles={
    'user-profile': MemoryProfile(
      profile={
        'technical_stack': 'ADK'
      },
      schema_id='user-profile'
    )
  }
)
"""

בסדרת האירועים הבאה שנשלחת אל Memory Bank, המשתמש מציין שהוא סטודנט ושהוא מתכנת בעיקר ב-Python:

client.agent_engines.memories.generate(
  name=memory_bank.api_resource.name,
  scope={"user_id": "123"},
  direct_contents_source={
    "events": [
      {"content": {
        "parts": [
          {"text": "Do you have any career recommendations for students that specialize in Python?"}]}}]
  }
)

התכונה Memory Bank מחלצת מהאינטראקציה את המילה Python ואת הסטטוס student (סטודנט). הקטע technical_stack מאוחד, והמחרוזת 'Python' מצורפת לרשומה הקיימת 'ADK'. המערכת מאכלסת את השדה job_status שהיה ריק קודם לכן באמצעות ה-enum ‏student (תלמיד/ה) ומדלגת על שלב האיחוד.

result = client.agent_engines.memories.retrieve_profiles(
    name=memory_bank.api_resource.name,
    scope=scope
)

"""
Returns:

RetrieveProfilesResponse(
  profiles={
    'user-profile': MemoryProfile(
      profile={
        'technical_stack': 'ADK, Python',
        'job_status': 'student'
      },
      schema_id='user-profile'
    )
  }
)
"""

אחזור פרופיל

אחרי שהפרופיל המאוחד נוצר, אפשר לאחזר אותו עבור היקף ספציפי באמצעות השיטה RetrieveProfiles. כך תקבלו את הנתונים העדכניים ביותר שמופים לסכימה שלכם.

result = client.agent_engines.memories.retrieve_profiles(
  name=memory_bank.api_resource.name,
  scope={"user_id": "123"},
)

# Accessing the data
for profile in result.profiles.values():
  print(profile)
  # Output: {'technical_stack': 'ADK, Python', 'job_status': 'student', ...}

בדיקת פרופיל

מתחת לפני השטח, הפרופיל מורכב מזיכרונות בודדים מסוג STRUCTURED_PROFILE. כל שדה בסכימה ממופה לזיכרון יחיד לכל היקף, מה שמאפשר יכולת תצפית גרנולרית.

RetrieveProfiles היא השיטה העיקרית לאחזור פרופילים בייצור, אבל אפשר לבדוק זיכרונות בודדים כדי לבדוק את ההתפתחות של הפרופיל. הגישה כוללת את הפריטים הבאים:

  • מטא-נתונים ברמת השדה: אפשר להציג ולעדכן את הזמן הספציפי לחיים (TTL) ואת Memory.metadata עבור שדות ספציפיים בפרופיל.
  • היסטוריית הגרסאות: אפשר לעקוב אחרי שושלת נתונים של שדה כדי לראות את הערכים ההיסטוריים ואת ההקשר הספציפי של השיחה שהפעיל כל שינוי.

לדוגמה, אפשר להשתמש ב-RetrieveMemories כדי לאחזר את כל הזיכרונות שמכילים קטעים מהפרופיל של משתמש. כברירת מחדל, RetrieveMemories מאחזר רק זיכרונות בשפה טבעית, לכן צריך לבקש במפורש זיכרונות של STRUCTURED_PROFILE:

client.agent_engines.memories.retrieve(
  name="...",
  scope={"user_id": "123"},
  config={
    "memory_types": ["STRUCTURED_PROFILE"]
  }
)

"""
Returns:

[RetrieveMemoriesResponseRetrievedMemory(
   memory=Memory(
     create_time=datetime.datetime(...),
     memory_type=<MemoryType.STRUCTURED_PROFILE: 'STRUCTURED_PROFILE'>,
     name='projects/.../locations/.../reasoningEngines/.../memories/...',
     scope={
       'user_id': '1'
     },
     structured_content=MemoryStructuredContent(
       data={
         'technical_stack': 'ADK, Python'
       },
       schema_id='user'
     ),
     update_time=datetime.datetime(...)
   )
 )]
"""

לאחר מכן, תוכלו לאחזר את היסטוריית הגרסאות של קטע הפרופיל המובנה הזה כדי לבדוק איך שדה הפרופיל השתנה לאורך זמן ואת ההקשר של כל אחד מהשינויים האלה:

for retrieved_memory in list(results):
    list(client.agent_engines.memories.revisions.list(
        name=retrieved_memory.memory.name
    ))

"""
Returns:

[MemoryRevision(
   create_time=datetime.datetime(...),
   expire_time=datetime.datetime(...),
   extracted_memories=[
     IntermediateExtractedMemory(
       context='The user indicated that they have expertise in Python when asking about career options.',
       structured_data={
         'technical_stack': 'Python'
       }
     ),
   ],
   name='projects/.../locations/.../reasoningEngines/.../memories/.../revisions/...',
   structured_data={
     'technical_stack': 'ADK, Python'
   }
 ),
 MemoryRevision(
   create_time=datetime.datetime(...),
   expire_time=datetime.datetime(...),
   extracted_memories=[
     IntermediateExtractedMemory(
       context='The user indicated that they need help building an ADK agent',
       structured_data={
         'technical_stack': 'ADK'
       }
     ),
   ],
   name='projects/.../locations/.../reasoningEngines/.../memories/.../revisions/...',
   structured_data={
     'technical_stack': 'ADK'
   }
 )]
"""