Puedes usar Memory Bank para generar perfiles estructurados, que son estructuras de datos con esquemas estáticos que se propagan y actualizan con LLMs. Si defines un esquema fijo, te aseguras de que tus agentes tengan acceso inmediato y de baja latencia a la información en evolución sin necesidad de realizar operaciones de búsqueda costosas durante una sesión.
Para completar los pasos que se muestran en esta guía, primero debes seguir los pasos que se indican en Configuración para Memory Bank.
Descripción general
El uso de perfiles estructurados con tu agente garantiza que la información extraída, como las preferencias o las pilas técnicas de un usuario, esté disponible de forma rápida, concisa y con un formato coherente. Por ejemplo, puedes recuperar perfiles que incluyan contenido como el siguiente:
MemoryProfile(
profile={
"technical_stacks": "ADK, Python",
"preferred_language": "Python",
"tone_preference": "Succinct"
},
schema_id="user-profile"
)
Los perfiles estructurados están optimizados para la recuperación de baja latencia, ya que el trabajo arduo de seleccionar la información se realiza en el momento de la generación. Son ideales para inicializar una interacción entre un agente y un usuario.
Información sobre los perfiles de memoria estructurados
Los perfiles estructurados se generan a través de los mismos métodos (GenerateMemories y IngestEvents) que usas para las memorias de lenguaje natural. Una vez que definas un esquema, Memory Bank intentará generar automáticamente perfiles que se alineen con tu esquema usando las fuentes de datos proporcionadas.
Cuando se usan perfiles estructurados, Memory Bank realiza las siguientes operaciones durante la generación de memoria:
- Extracción: Extrae información y contexto que se ajustan al esquema de la fuente de datos. Solo se extraerá la información que se alinee con el esquema. Puedes inspeccionar qué información y contexto se extrajeron con las revisiones de memoria.
- Consolidación: Actualiza (si es necesario) los campos existentes en el perfil. Un LLM juzgará cómo actualizar el contenido existente en función de la información y el contexto recién extraídos. Si el campo aún no existe en el perfil, se omitirá la consolidación y el campo se actualizará directamente con la información extraída.
Los perfiles se aíslan según el scope (como {"user_id": "123"}) que proporcionaste cuando transferiste datos a Memory Bank. Para cada esquema y alcance, Memory Bank mantiene un solo perfil como fuente de información. Un perfil generado se compone de una o más instancias de Memory. Cada instancia de Memory representa un solo campo dentro del perfil que puedes usar para inspeccionar los metadatos y el historial de revisiones del campo, como los siguientes:
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={...}
)
Definición de esquema
Los perfiles que genera Memory Bank se alinean con el esquema que se definió cuando se creó o actualizó la instancia de Agent Platform. Puedes usar un modelo pydantic para definir los campos que deseas que Memory Bank extraiga y mantenga. Por ejemplo:
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")
Sube el esquema a Memory Bank cuando crees o actualices tu instancia de Agent Platform. Puedes definir varios esquemas de perfil independientes. Cada esquema debe identificarse con un ID único:
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]
}
]
}
}
}
)
De forma predeterminada, Memory Bank siempre intentará extraer memorias de lenguaje natural. Puedes inhabilitar la generación de memoria de lenguaje natural si solo deseas que Memory Bank genere perfiles:
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}
]
}
}
}
)
Generación de perfiles
Para activar la generación de memorias estructuradas, proporciona un historial de conversaciones (eventos) a los métodos GenerateMemories o IngestEvents. Al igual que con las memorias de lenguaje natural, Memory Bank usa LLMs para extraer información significativa de las fuentes de datos y consolidarla con las memorias existentes.
Ejemplo
En el siguiente ejemplo, se explica la generación de perfiles de memoria con el esquema definido anteriormente.
En el primer conjunto de eventos enviados a Memory Bank para el alcance {"user_id": "123"}, el usuario indica que trabaja con agentes de 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 extrae "ADK" para el campo technical_stack en el esquema. No se propagan otros campos porque el evento transferido no contiene información pertinente para el resto del esquema. Como esta es la primera interacción para este alcance, se omite la consolidación y el perfil se inicializa con este valor inicial.
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'
)
}
)
"""
En el siguiente conjunto de eventos enviados a Memory Bank, el usuario indica que es estudiante y que programa principalmente en 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 extrae "Python" y el estado "estudiante" de la interacción. Se consolida el fragmento technical_stack y se agrega "Python" a la entrada "ADK" existente. El sistema propaga el campo job_status que estaba vacío con la enumeración "estudiante" y omite el paso de consolidación.
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'
)
}
)
"""
Recuperación de perfiles
Una vez que se genera, puedes recuperar el perfil consolidado para un alcance específico con el método RetrieveProfiles. Esto muestra los datos más actualizados asignados a tu esquema.
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', ...}
Inspección de perfiles
En segundo plano, un perfil se compone de memorias individuales de tipo STRUCTURED_PROFILE. Cada campo de tu esquema se asigna a una sola memoria por alcance, lo que permite una observabilidad detallada.
Si bien RetrieveProfiles es el método principal para la recuperación de perfiles de producción, puedes inspeccionar memorias individuales para auditar la evolución del perfil. Esto proporciona acceso a lo siguiente:
- Metadatos a nivel del campo: Consulta y actualiza el tiempo de actividad (TTL) y
Memory.metadataespecíficos para campos individuales en el perfil. - Historial de revisiones: Realiza un seguimiento del linaje de un campo para ver los valores históricos y el contexto de conversación específico que activó cada cambio.
Por ejemplo, puedes usar RetrieveMemories para recuperar todas las memorias que contengan fragmentos del perfil de un usuario. De forma predeterminada, RetrieveMemories solo recupera memorias de lenguaje natural, por lo que debes solicitar explícitamente las memorias 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(...)
)
)]
"""
Luego, puedes recuperar el historial de revisiones de este fragmento de perfil estructurado para inspeccionar cómo cambió el campo del perfil con el tiempo y el contexto en torno a cada uno de esos cambios:
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'
}
)]
"""