메모리 프로필

메모리 뱅크를 사용하여 구조화된 프로필을 생성할 수 있습니다. 구조화된 프로필은 LLM을 사용하여 채워지고 업데이트되는 정적 스키마가 있는 데이터 구조입니다. 고정 스키마를 정의하면 에이전트가 세션 중에 비용이 많이 드는 검색 작업을 수행할 필요 없이 진화하는 정보에 즉시 짧은 지연 시간으로 액세스할 수 있습니다.

이 가이드에 설명된 단계를 완료하려면 먼저 메모리 뱅크 설정의 단계를 수행해야 합니다.

개요

에이전트와 함께 구조화된 프로필을 사용하면 사용자의 기술 스택 또는 환경설정과 같은 추출된 정보를 일관된 형식으로 빠르고 간결하게 사용할 수 있습니다. 예를 들어 다음과 같은 콘텐츠가 포함된 프로필을 검색할 수 있습니다.

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

구조화된 프로필은 정보 큐레이션의 어려운 작업이 생성 시간에 수행되므로 지연 시간이 짧은 검색에 최적화되어 있습니다. 에이전트와 사용자 간의 상호작용을 초기화하는 데 적합합니다.

구조화된 메모리 프로필 이해

구조화된 프로필은 자연어 메모리에 사용하는 것과 동일한 메서드 (GenerateMemoriesIngestEvents)를 통해 생성됩니다. 스키마를 정의하면 메모리 뱅크는 제공된 데이터 소스를 사용하여 스키마에 맞는 프로필을 자동으로 생성하려고 시도합니다.

구조화된 프로필을 사용할 때 메모리 뱅크는 메모리 생성 중에 다음 작업을 수행합니다.

  • 추출: 데이터 소스에서 스키마에 맞는 정보와 컨텍스트를 추출합니다. 스키마에 맞는 정보만 추출됩니다. 메모리 수정사항을 사용하여 추출된 정보와 컨텍스트를 검사할 수 있습니다.
  • 통합: 필요한 경우 프로필의 기존 필드를 업데이트합니다. LLM은 새로 추출된 정보와 컨텍스트를 기반으로 기존 콘텐츠를 업데이트하는 방법을 판단합니다. 필드가 프로필에 아직 없는 경우 통합이 건너뛰고 필드가 추출된 정보로 직접 업데이트됩니다.

프로필은 데이터를 메모리 뱅크로 수집할 때 제공한 scope (예: {"user_id": "123"})를 기반으로 격리됩니다. 각 스키마 및 범위에 대해 메모리 뱅크는 단일 프로필을 정보 소스로 유지합니다. 생성된 프로필은 하나 이상의 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={...}
)

스키마 정의

메모리 뱅크에서 생성하는 프로필은 Agent Platform 인스턴스가 생성되거나 업데이트될 때 정의된 스키마와 일치합니다. pydantic 모델을 사용하여 메모리 뱅크에서 추출하고 유지할 필드를 정의할 수 있습니다. 예를 들면 다음과 같습니다.

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")

Agent Platform 인스턴스를 만들거나 업데이트할 때 스키마를 메모리 뱅크에 업로드합니다. 여러 독립적인 프로필 스키마를 정의할 수 있습니다. 각 스키마는 고유한 ID로 식별되어야 합니다.

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

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

기본적으로 메모리 뱅크는 항상 자연어 메모리를 추출하려고 시도합니다. 메모리 뱅크에서 프로필만 생성하도록 하려면 자연어 메모리 생성을 사용 중지하면 됩니다.

agent_engine = 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 메서드에 제공합니다. 자연어 메모리와 마찬가지로 메모리 뱅크는 LLM을 사용하여 데이터 소스에서 의미 있는 정보를 추출하고 기존 메모리와 통합합니다.

예시

다음 예시에서는 이전에 정의된 스키마를 사용하여 메모리 프로필 생성을 안내합니다.

{"user_id": "123"} 범위에 대해 메모리 뱅크로 전송된 첫 번째 이벤트 집합에서 사용자는 ADK 에이전트를 사용한다고 나타냅니다.

client.agent_engines.memories.generate(
  name=agent_engine.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?"}]}}]
  }
)

메모리 뱅크는 스키마의 technical_stack 필드에 대해 'ADK'를 추출합니다. 수집된 이벤트에 나머지 스키마와 관련된 정보가 포함되어 있지 않으므로 다른 필드는 채워지지 않습니다. 이 범위의 첫 번째 상호작용이므로 통합이 건너뛰고 프로필이 이 초기 값으로 초기화됩니다.

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

"""
Returns:

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

메모리 뱅크로 전송된 다음 이벤트 집합에서 사용자는 학생이며 주로 Python으로 코딩한다고 나타냅니다.

client.agent_engines.memories.generate(
  name=agent_engine.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?"}]}}]
  }
)

메모리 뱅크는 상호작용에서 'Python'과 '학생' 상태를 모두 추출합니다. technical_stack 프래그먼트가 통합되어 기존 'ADK' 항목에 'Python'이 추가됩니다. 시스템은 이전에 비어 있던 job_status 필드를 '학생' enum으로 채우고 통합 단계를 건너뜁니다.

result = client.agent_engines.memories.retrieve_profiles(
    name=agent_engine_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=agent_engine.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'
   }
 )]
"""