Vous pouvez utiliser Memory Bank pour générer des profils structurés, qui sont des structures de données avec des schémas statiques remplis et mis à jour à l'aide de LLM. En définissant un schéma fixe, vous vous assurez que vos agents ont un accès immédiat et à faible latence aux informations en constante évolution, sans avoir besoin d'opérations de recherche coûteuses pendant une session.
Pour suivre les étapes décrites dans ce guide, vous devez d'abord suivre les étapes de la section Configurer Memory Bank.
Présentation
L'utilisation de profils structurés avec votre agent garantit que les informations extraites, telles que les piles techniques ou les préférences d'un utilisateur, sont disponibles rapidement, de manière concise et dans un format cohérent. Par exemple, vous pouvez récupérer des profils qui incluent du contenu tel que :
MemoryProfile(
profile={
"technical_stacks": "ADK, Python",
"preferred_language": "Python",
"tone_preference": "Succinct"
},
schema_id="user-profile"
)
Les profils structurés sont optimisés pour une récupération à faible latence, car le travail de curation des informations est effectué au moment de la génération. Ils sont idéaux pour initialiser une interaction entre un agent et un utilisateur.
Comprendre les profils de mémoire structurés
Les profils structurés sont générés à l'aide des mêmes méthodes (GenerateMemories et IngestEvents) que celles que vous utilisez pour les mémoires en langage naturel. Une fois que vous avez défini un schéma, Memory Bank tente automatiquement de générer des profils qui correspondent à votre schéma à l'aide des sources de données fournies.
Lorsque vous utilisez des profils structurés, Memory Bank effectue les opérations suivantes lors de la génération de la mémoire :
- Extraction : extrait les informations et le contexte qui correspondent au schéma de la source de données. Seules les informations qui correspondent au schéma sont extraites. Vous pouvez inspecter les informations et le contexte extraits à l'aide des révisions de la mémoire.
- Consolidation : met à jour (si nécessaire) les champs existants dans le profil. Un LLM détermine comment mettre à jour le contenu existant en fonction des informations et du contexte nouvellement extraits. Si le champ n'existe pas déjà dans le profil, la consolidation est ignorée et le champ est mis à jour directement avec les informations extraites.
Les profils sont isolés en fonction du scope (par exemple, {"user_id": "123"}) que vous avez fourni lors de l'ingestion de données dans Memory Bank. Pour chaque schéma et champ d'application, Memory Bank conserve un seul profil comme source de vérité. Un profil généré est composé d'une ou de plusieurs instances Memory. Chaque instance Memory représente un seul champ dans le profil que vous pouvez utiliser pour inspecter les métadonnées et l'historique des révisions du champ, par exemple :
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={...}
)
Définition du schéma
Les profils générés par Memory Bank correspondent au schéma défini lors de la création ou de la mise à jour de l'instance Agent Platform. Vous pouvez utiliser un modèle pydantic pour définir les champs que vous souhaitez que Memory Bank extrait et conserve. Exemple :
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")
Importez le schéma dans Memory Bank lorsque vous créez ou mettez à jour votre instance Agent Platform. Vous pouvez définir plusieurs schémas de profil indépendants. Chaque schéma doit être identifié par un ID unique :
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]
}
]
}
}
}
)
Par défaut, Memory Bank tente toujours d'extraire des mémoires en langage naturel. Vous pouvez désactiver la génération de la mémoire en langage naturel si vous souhaitez que Memory Bank génère uniquement des profils :
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}
]
}
}
}
)
Génération de profils
Vous déclenchez la génération de mémoires structurées en fournissant un historique des conversations (événements) aux méthodes GenerateMemories ou IngestEvents. Comme pour les mémoires en langage naturel, Memory Bank utilise des LLM pour extraire des informations significatives des sources de données et les consolider avec les mémoires existantes.
Exemple
L'exemple suivant décrit la génération de profils de mémoire à l'aide du schéma défini précédemment.
Dans le premier ensemble d'événements envoyés à Memory Bank pour le champ d'application {"user_id": "123"}, l'utilisateur indique qu'il travaille avec des agents 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 extrait "ADK" pour le champ technical_stack dans le schéma. Aucun autre champ n'est renseigné, car l'événement ingéré ne contient pas d'informations pertinentes pour le reste du schéma. Comme il s'agit de la première interaction pour ce champ d'application, la consolidation est ignorée et le profil est initialisé avec cette valeur initiale.
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'
)
}
)
"""
Dans l'ensemble d'événements suivant envoyé à Memory Bank, l'utilisateur indique qu'il est étudiant et qu'il code principalement 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 extrait à la fois "Python" et le statut "étudiant" de l'interaction. Le fragment technical_stack est consolidé, ajoutant "Python" à l'entrée "ADK" existante. Le système remplit le champ job_status précédemment vide avec l'énumération "étudiant" et ignore l'étape de consolidation.
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'
)
}
)
"""
Récupération de profils
Une fois généré, vous pouvez récupérer le profil consolidé pour un champ d'application spécifique à l'aide de la méthode RetrieveProfiles. Cette méthode renvoie les données les plus récentes mappées à votre schéma.
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', ...}
Inspection de profils
En interne, un profil est composé de mémoires individuelles de type STRUCTURED_PROFILE. Chaque champ de votre schéma est mappé à une seule mémoire par champ d'application, ce qui permet une observabilité précise.
Bien que RetrieveProfiles soit la méthode principale pour la récupération de profils en production, vous pouvez inspecter des mémoires individuelles pour auditer l'évolution du profil. Cela permet d'accéder aux éléments suivants :
- Métadonnées au niveau du champ : affichez et mettez à jour la valeur TTL (Time To Live) et
Memory.metadataspécifiques pour les champs individuels du profil. - Historique des révisions : suivez la traçabilité d'un champ pour afficher les valeurs historiques et le contexte de conversation spécifique qui a déclenché chaque modification.
Par exemple, vous pouvez utiliser RetrieveMemories pour récupérer toutes les mémoires contenant des fragments du profil d'un utilisateur. Par défaut, RetrieveMemories ne récupère que les mémoires en langage naturel. Vous devez donc demander explicitement les mémoires 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(...)
)
)]
"""
Vous pouvez ensuite récupérer l'historique des révisions de ce fragment de profil structuré pour inspecter l'évolution du champ de profil au fil du temps et le contexte de chacune de ces modifications :
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'
}
)]
"""