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 renseignés 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'effectuer des opérations de recherche coûteuses pendant une session.
Pour suivre les étapes décrites dans ce guide, vous devez d'abord suivre celles de la section Configurer Memory Bank.
Présentation
L'utilisation de profils structurés avec votre agent permet de s'assurer 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 incluant 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 la récupération à faible latence, car le travail de curation des informations est effectué au moment de la génération. Ils sont parfaits pour lancer une interaction entre un agent et un utilisateur.
Comprendre les profils de mémoire structurée
Les profils structurés sont générés à l'aide des mêmes méthodes (GenerateMemories et IngestEvents) que celles utilisées pour les souvenirs 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 seront 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 du profil. Un LLM déterminera comment mettre à jour le contenu existant en fonction des nouvelles informations et du nouveau contexte extraits. Si le champ n'existe pas déjà dans le profil, la consolidation est ignorée et le champ est directement mis à jour avec les informations extraites.
Les profils sont isolés en fonction du scope (comme {"user_id": "123"}) que vous avez fourni lors de l'ingestion de données dans Memory Bank. Pour chaque schéma et portée, Memory Bank conserve un seul profil comme source de référence. Un profil généré est composé d'une ou plusieurs instances Memory. Chaque instance Memory représente un champ unique dans le profil. Vous pouvez l'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 sont conformes 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()
}
agent_engine = client.agent_engines.create(
config={
"context_spec": {
"memory_bank_config": {
"structured_memory_configs": [
{
"schema_configs": [schema_config]
}
]
}
}
}
)
Par défaut, Memory Bank essaie toujours d'extraire des souvenirs en langage naturel. Vous pouvez désactiver la génération de mémoire en langage naturel si vous souhaitez que Memory Bank ne génère que des profils :
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}
]
}
}
}
)
Génération de profils
Vous déclenchez la génération de souvenirs structurés en fournissant un historique des conversations (événements) aux méthodes GenerateMemories ou IngestEvents. Comme pour les souvenirs en langage naturel, Memory Bank utilise des LLM pour extraire des informations pertinentes des sources de données et les consolider avec les souvenirs existants.
Exemple
L'exemple suivant explique comment générer un profil 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=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?"}]}}]
}
)
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 cette portée, la consolidation est ignorée et le profil est initialisé avec cette valeur initiale.
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'
)
}
)
"""
Dans l'ensemble d'événements suivant envoyés à la Memory Bank, l'utilisateur indique qu'il est étudiant et qu'il code principalement en 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?"}]}}]
}
)
Memory Bank extrait à la fois "Python" et l'état "étudiant" de l'interaction. Le fragment technical_stack est consolidé, en ajoutant "Python" à l'entrée "ADK" existante. Le système remplit le champ job_status, qui était vide, avec l'énumération "student" et ignore l'étape de consolidation.
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'
)
}
)
"""
Récupération du profil
Une fois le profil consolidé généré, vous pouvez le récupérer pour un champ d'application spécifique à l'aide de la méthode RetrieveProfiles. Cela renvoie les données les plus récentes mappées à votre schéma.
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', ...}
Examen du profil
En interne, un profil est composé de souvenirs individuels de type STRUCTURED_PROFILE. Chaque champ de votre schéma correspond à un seul souvenir par portée, ce qui permet une observabilité précise.
Bien que RetrieveProfiles soit la principale méthode de récupération des profils en production, vous pouvez inspecter des souvenirs individuels pour auditer l'évolution du profil. Vous aurez accès aux éléments suivants :
- Métadonnées au niveau des champs : 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 tous les souvenirs contenant des fragments du profil d'un utilisateur. Par défaut, RetrieveMemories ne récupère que les souvenirs en langage naturel. Vous devez donc demander explicitement les souvenirs 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'
}
)]
"""