Utiliser des images et des messages multimodaux
Les agents ont souvent besoin de mémoriser des images ainsi que du texte. Les captures d'écran, les documents, les graphiques et les photographies peuvent contenir des détails que la mémoire en texte seul ne peut pas conserver.
Dans ce guide, vous apprendrez à :
- ajouter des images autonomes et joindre des images aux messages de fil ;
- rechercher les deux types d'images ;
- récupérer les octets dʼimage dʼorigine ;
- Configurer l'extraction automatique de la mémoire pour utiliser le texte des messages, les descriptions des images ou les images d'origine.
Conseil : pour la configuration des packages, reportez-vous à Introduction à la mémoire de l'agent. Si vous avez besoin d'une base de données Oracle AI Database locale pour cet exemple, suivez Exécuter Oracle AI Database localement.
Créer un client OracleAgentMemory
Créez le client OracleAgentMemory utilisé par les exemples de ce guide. Il se connecte à Oracle AI Database, configure un intégrateur et un LLM compatible avec la vision et définit des limites pour l'entrée d'image. Reportez-vous à la section Formats de révision, limites et gestion des données à la fin de ce guide pour une explication de ces limites.
from pathlib import Path
import oracledb
from oracleagentmemory.apis.message import ImageContent, ImageMimeType, Message, TextContent
from oracleagentmemory.core import (
ImageInputLimitConfig,
MemoryExtractionConfig,
MemoryExtractionImageContext,
MemoryExtractionMode,
OracleAgentMemory,
SchemaPolicy,
)
from oracleagentmemory.core.embedders.embedder import Embedder
from oracleagentmemory.core.llms.llm import Llm
embedder = Embedder(model="YOUR_EMBEDDING_MODEL")
vision_llm = Llm(
model="YOUR_VISION_CAPABLE_MODEL",
supports_vision=True,
)
db_pool = oracledb.SessionPool(
user="YOUR DB USER",
password="YOUR DB PASSWORD",
dsn="localhost:1521/...",
)
memory_store_id = "T_IMAGE_GUIDE"
memory = OracleAgentMemory(
connection=db_pool,
embedder=embedder,
llm=vision_llm,
memory_extraction_config=MemoryExtractionConfig(
extract_memories=False,
extraction_mode=MemoryExtractionMode.BACKGROUND,
),
image_input_limit_config=ImageInputLimitConfig(
max_raw_image_bytes=10 * 1024 * 1024,
max_images_per_llm_request=20,
max_total_raw_image_bytes_per_llm_request=50 * 1024 * 1024,
),
schema_policy=SchemaPolicy.CREATE_IF_NECESSARY,
memory_store_id=memory_store_id,
)
image_bytes = Path("sample.png").read_bytes()
Le client définit supports_vision=True lorsqu'il crée Llm. Définissez cette option uniquement lorsque le modèle et l'adresse de fournisseur sélectionnés acceptent l'entrée d'image. Si vous l'omettez, Llm vérifie les métadonnées du modèle. Lorsqu'aucune métadonnée n'est disponible, Llm envoie une petite image de test pour déterminer si l'adresse accepte l'entrée d'image. La définition de supports_vision=True ignore les deux vérifications ; elle ne permet pas à un modèle de type texte uniquement de traiter des images.
| Référence d'API : Llm | Configuration de limite d'entrée d'image | Configuration d'extraction de mémoire |
Stocker une image autonome
Utilisez OracleAgentMemory.add_image() pour ajouter une image autonome. Incluez au moins l'un des éléments suivants : user_id, agent_id ou thread_id. Vous devez fournir un identificateur correspondant lorsque vous appelez ultérieurement list_images() ou search().
Pour la recherche, le kit SDK représente une image avec une description textuelle. Avec la recherche vectorielle, il intègre cette description avec le même intégrateur de texte utilisé pour la recherche de texte uniquement ; il n'intègre pas les octets d'image. Si vous transmettez description à add_image(), le kit SDK utilise ce texte. Si vous l'omettez, le LLM compatible avec la vision configuré génère automatiquement la description. L'exemple suivant fournit une description. Par conséquent, l'ajout de l'image n'effectue pas de demande LLM.
image_id = memory.add_image(
image_bytes,
mime_type=ImageMimeType.PNG,
user_id="customer_123",
description="A red sample image used by the image support guide.",
metadata={"source": "profile-photo"},
)
images = memory.list_images(user_id="customer_123", limit=10)
stored_image = next(image for image in images if image.id == image_id)
print(stored_image.content)
list_images() renvoie les images autonomes qui correspondent aux valeurs user_id, agent_id ou thread_id fournies. Chaque valeur ImageRecord.content contient la description de l'image.
Pour associer une image autonome à un thread, appelez OracleThread.add_image(). Cette méthode utilise l'ID de thread et tous les ID utilisateur ou agent stockés sur le thread, de sorte que vous ne les transmettez pas à nouveau.
| Référence d'API : OracleAgentMemory | OracleThread | Enregistrement d'image |
Stocker une image dans le cadre d'un message
Stockez une image dans le cadre d'un message de thread lorsqu'elle fournit un contexte pour un virage de conversation et qu'elle n'a pas besoin d'être gérée séparément.
Un message de type texte uniquement peut utiliser une chaîne pour content. Pour un message qui contient du texte et des images, transmettez une liste ordonnée de parties de contenu. Le kit SDK conserve cet ordre lorsqu'il stocke et extrait le message et lorsqu'il crée une invite d'extraction de mémoire.
Sous la forme d'un dictionnaire, chaque partie de contenu a une valeur type :
- Une partie de texte est
{"type": "text", "text": "..."}. - Une partie d'image contient
"type": "image",bytesbrut etmime_type. Il peut également contenir un élémentdescription.
Les valeurs mime_type prises en charge sont "image/png", "image/jpeg" et "image/webp".
thread = memory.create_thread(
thread_id="image-support-thread",
user_id="customer_123",
memory_extraction_config=MemoryExtractionConfig(
extract_memories=False,
extraction_mode=MemoryExtractionMode.BACKGROUND,
),
)
message_id = thread.add_messages(
[
{
"role": "user",
"content": [
{"type": "text", "text": "Please remember what is in this image."},
{
"type": "image",
"bytes": image_bytes,
"mime_type": "image/png",
},
],
}
]
)[0]
Dans cet exemple, le dictionnaire d'images omet description, de sorte que le kit SDK en génère un avec le LLM compatible avec la vision configuré.
Le client utilise BACKGROUND, de sorte que le kit SDK stocke le message, met en file d'attente la génération de la description et le renvoie avant la fin de la génération. Utilisez INLINE lorsque la description doit être prête pour que add_messages() renvoie.
Appelez wait_for_memory_extraction() avant de lire ou de rechercher la description générée. La méthode attend les tâches de description d'image et d'extraction de mémoire mises en file d'attente par ce client.
Une fois la génération réussie, get_message() renvoie la description dans le champ ImageContent.description de la partie d'image :
memory.wait_for_memory_extraction()
stored_message = thread.get_message(message_id)
attached_image = next(
part for part in stored_message.content if isinstance(part, ImageContent)
)
print(attached_image.description)
Si la génération échoue ou que la file d'attente rejette la tâche, l'image reste stockée sans description. Vérifiez ImageContent.description avant d'utiliser le texte généré.
Vous pouvez également créer le message avec des objets TextContent et ImageContent au lieu de dictionnaires. Les deux formulaires stockent le même message.
typed_message_id = thread.add_messages(
[
Message(
role="user",
content=[
TextContent(text="This message uses the typed content API."),
ImageContent(
bytes=image_bytes,
mime_type=ImageMimeType.PNG,
description="A red square on a white background.",
),
],
)
]
)[0]
print(typed_message_id)
Etant donné que ce fichier ImageContent inclut une description, le kit SDK n'envoie pas l'image à un LLM pour la génération de descriptions.
| Référence d'API : Llm | Configuration d'extraction de mémoire | Messages et contenu des messages | OracleThread |
Rechercher des images
Définissez record_types=["image"] pour rechercher les descriptions d'image. Il recherche à la fois les images autonomes et les images jointes aux messages. Chaque résultat est une valeur ImageRecord sans les octets d'image d'origine.
Vous pouvez fournir la description vous-même ou laisser le LLM compatible avec la vision configuré la générer. Le kit SDK stocke et recherche les descriptions générées de la même manière que les descriptions fournies.
Avec la stratégie VECTOR par défaut utilisée dans ce guide, le kit SDK divise une description d'image en blocs si nécessaire et intègre ces blocs avec le fichier Embedder configuré. Il intègre la requête avec le même incorporateur et compare les vecteurs résultants. Ce processus est le même pour les images autonomes et les images associées aux messages.
Les autres stratégies de recherche traitent les descriptions différemment. KEYWORD recherche le texte de description stocké sans créer d'intégrations. HYBRID combine la correspondance de texte avec les vecteurs produits par son OracleDBEmbedder configuré.
L'exemple suivant recherche les deux images. Chaque résultat inclut l'ID d'image. Une image jointe inclut également l'ID de son message parent.
standalone_matches = memory.search(
"red sample image",
user_id="customer_123",
record_types=["image"],
max_results=5,
)
for match in standalone_matches:
print(match.record.id, match.content)
attached_matches = thread.search(
"red square on a white background",
record_types=["image"],
max_results=5,
)
for match in attached_matches:
print(match.record.message_id, match.record.id, match.content)
Les sections d'extraction suivantes utilisent ces ID pour charger les octets d'origine.
Avec record_types=["message"], la recherche examine uniquement le texte du message. Il ne recherche pas les descriptions des images jointes aux messages ; utilisez record_types=["image"] pour ces descriptions.
| Référence d'API : OracleAgentMemory | OracleThread | OracleSearchResult | Emballage |
Extraire les octets d'image autonome
Par défaut, list_images() renvoie les métadonnées et les descriptions d'image, mais pas les octets stockés. Pour extraire les octets, définissez include_bytes=True et fournissez l'ID d'image à partir du résultat de recherche ainsi qu'une valeur user_id, agent_id ou thread_id correspondante. Ces filtres limitent la demande à une image.
#Raw bytes are omitted by default. To load them, use the image ID returned by
#the search result and pass the same user, agent, or thread ID used when
#adding it.
standalone_match = next(
match for match in standalone_matches if match.record.id == image_id
)
loaded_image = memory.list_images(
image_id=standalone_match.record.id,
user_id="customer_123",
include_bytes=True,
)[0]
| Référence d'API : OracleAgentMemory | Enregistrement d'image |
Extraire les octets d'une image jointe à un message
Par défaut, les parties d'image renvoyées par get_message() et get_messages() n'incluent pas leurs octets. Pour extraire une image, transmettez son ID du résultat de recherche à get_message(..., included_image_ids=[...]). Le résultat de recherche message_id identifie le message à extraire.
#get_message() omits image bytes unless included_image_ids selects them.
attached_match = next(
match
for match in attached_matches
if match.record.message_id == typed_message_id
)
message_with_bytes = thread.get_message(
attached_match.record.message_id,
included_image_ids=[attached_match.record.id],
)
hydrated_image = next(
part for part in message_with_bytes.content if isinstance(part, ImageContent)
)
| Référence d'API : Messages et contenu des messages | OracleThread |
Gérer les images jointes aux messages
Une image jointe appartient à son message parent. Pour remplacer ou enlever une image jointe, appelez update_message() avec le nouveau contenu du message. La suppression du message supprime également toutes les images qui lui sont associées. delete_image() supprime uniquement les images autonomes. Vous ne pouvez pas mettre à jour directement la durée de vie d'une image jointe.
Lorsque vous ajoutez une image jointe, elle reçoit l'heure d'expiration du message parent. Si update_message() fait expirer le message plus tôt, le kit SDK raccourcit également le délai d'expiration de l'image. L'extension du délai d'expiration du message, ou son effacement avec ttl_days=None, ne prolonge ni n'efface le délai d'expiration déjà stocké pour l'image. Les recherches et les lectures excluent l'image une fois que l'image ou son message parent a expiré.
| Référence d'API : Messages et contenu des messages | OracleThread |
Sélectionner les options d'extraction automatique de mémoire
Définissez MemoryExtractionConfig.memory_extraction_image_context pour contrôler les parties d'un message contenant les images que le LLM d'extraction de mémoire reçoit. Ce paramètre modifie uniquement l'invite d'extraction. Il ne modifie pas le message stocké ni ne génère une description d'image manquante.
Contexte d'image pour l'extraction automatique de mémoire
| Valeur | Ce que reçoit le LLM d'extraction | Sélectionnez-le quand |
|---|---|---|
DISABLED |
Seulement les parties du texte du message. | L'extraction doit ignorer les images. Il s'agit de la fonction par défaut. |
CAPTION |
Parties de texte et descriptions d'image, dans leur ordre d'origine. | Les descriptions contiennent les informations visuelles nécessaires à l'extraction, ou le fournisseur de LLM ne doit pas recevoir d'octets d'image. |
IMAGE |
Les parties de texte et les images originales, dans leur ordre d'origine. | Les souvenirs dépendent des détails visuels que les descriptions ne contiennent pas. Le LLM et son adresse doivent accepter l'entrée d'image. |
L'exemple suivant fait appel à CAPTION. Avec memory_extraction_frequency=1, l'extraction s'exécute après le premier message. Avec MemoryExtractionMode.INLINE, l'extraction se termine avant que add_messages() ne renvoie.
caption_thread = memory.create_thread(
thread_id="caption-extraction-thread",
user_id="customer_123",
memory_extraction_config=MemoryExtractionConfig(
extract_memories=True,
extraction_mode=MemoryExtractionMode.INLINE,
memory_extraction_frequency=1,
memory_extraction_image_context=MemoryExtractionImageContext.CAPTION,
),
)
caption_thread.add_messages(
[
Message(
role="user",
content=[
TextContent(text="Remember the color and shape in this image."),
ImageContent(
bytes=image_bytes,
mime_type=ImageMimeType.PNG,
description="A red square on a white background.",
),
],
)
]
)
Pour envoyer l'image d'origine, remplacez MemoryExtractionImageContext.CAPTION par MemoryExtractionImageContext.IMAGE. Le LLM configuré sur OracleAgentMemory doit accepter l'entrée d'image.
En mode CAPTION, chaque image envoyée pour extraction doit avoir une description non vide. Indiquez description lors de l'ajout de chaque image ou configurez un LLM compatible avec la vision pour générer les descriptions. Avec MemoryExtractionMode.BACKGROUND, le kit SDK met en file d'attente la génération de la description avant l'extraction de la mémoire pour le même thread. Si une image n'a toujours aucune description au début de l'extraction, le kit SDK rejette la demande.
N'utilisez pas MemoryExtractionImageContext.MEMORY. Cette valeur est réservée pour une utilisation future et le kit SDK la rejette.
| Référence d'API : MemoryExtractionImageContext | Configuration d'extraction de mémoire |
Mise à jour ou suppression d'une image autonome
Après avoir ajouté une image autonome, utilisez update_image() pour modifier sa description, ses métadonnées ou ses octets. La description est stockée dans ImageRecord.content et indexée pour la recherche. Transmettez une chaîne pour la remplacer ou transmettez None pour générer un remplacement avec le LLM de vision configuré. Si vous omettez description, la description existante reste inchangée.
memory.update_image(
image_id,
description="A red square used by the image support guide.",
metadata={"source": "reviewed-profile-photo"},
)
updated_image = memory.list_images(
image_id=image_id,
user_id="customer_123",
)[0]
print(updated_image.content)
print(updated_image.metadata)
deleted = memory.delete_image(image_id)
print(deleted)
L'exemple extrait l'image après update_image() pour vérifier la nouvelle description et les nouvelles métadonnées. Elle transmet ensuite image_id à delete_image() et vérifie qu'une image a été supprimée.
Pour remplacer les octets, transmettez image et mime_type ensemble. Dans le même appel, vous pouvez conserver la description actuelle, en fournir une nouvelle ou demander un remplacement généré par description=None. Contrairement à une image attachée à un message, une image autonome peut avoir ses propres métadonnées, horodatages et durée de vie.
| Référence d'API : OracleAgentMemory | OracleSearchResult |
Vérifier les formats, les limites et la gestion des données
Avant de stocker une image, le kit SDK décode ses octets et vérifie le format. Si vous transmettez mime_type, le format décodé doit correspondre. Le kit SDK accepte les images PNG, JPEG et WebP, mais rejette le PNG animé et le WebP animé. Vous ne pouvez pas désactiver cette validation.
Par défaut, une image brute peut atteindre 10 Mio. Une demande LLM unique peut contenir jusqu'à 100 images et 100 Mio de données d'image. Utilisez ImageInputLimitConfig pour réduire ces limites pour votre déploiement ou les augmenter jusqu'aux valeurs maximales documentées. Le client au début de ce guide permet 10 Mio par image, 20 images par demande et 50 Mio de données d'image par demande.
Oracle AI Agent Memory stocke les octets d'image dans Oracle AI Database. La génération d'une description envoie ces octets au fournisseur de LLM configuré. L'extraction de mémoire envoie également les octets en mode IMAGE. En mode CAPTION, l'extraction de mémoire envoie les descriptions d'image à la place. Une description peut révéler des informations de l'image d'origine. Passez en revue les remarques concernant la sécurité avant d'envoyer des images confidentielles à un LLM pour la génération de descriptions ou l'extraction de mémoire.
Conclusion
Dans ce guide, nous avons appris à ajouter des images autonomes, à joindre des images à des messages, à extraire des octets d'image, à rechercher des descriptions d'image et à configurer l'extraction automatique de la mémoire pour utiliser du texte de message, des descriptions d'image ou des images d'origine.
→ Après avoir appris à utiliser des images et des messages multimodaux, vous pouvez maintenant passer à Utiliser le délai de vie pour les messages et les mémoires.
Code complet
L'exemple complet est inclus dans ce guide pour que vous puissiez le copier et l'exécuter.
#Copyright © 2026 Oracle and/or its affiliates.
#This software is under the Apache License 2.0
#(LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License
#(UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option.
#Oracle Agent Memory Code Example - Use Images and Multimodal Messages
#---------------------------------------------------------------------
##Configure a vision capable memory client
from pathlib import Path
import oracledb
from oracleagentmemory.apis.message import ImageContent, ImageMimeType, Message, TextContent
from oracleagentmemory.core import (
ImageInputLimitConfig,
MemoryExtractionConfig,
MemoryExtractionImageContext,
MemoryExtractionMode,
OracleAgentMemory,
SchemaPolicy,
)
from oracleagentmemory.core.embedders.embedder import Embedder
from oracleagentmemory.core.llms.llm import Llm
embedder = Embedder(model="YOUR_EMBEDDING_MODEL")
vision_llm = Llm(
model="YOUR_VISION_CAPABLE_MODEL",
supports_vision=True,
)
db_pool = oracledb.SessionPool(
user="YOUR DB USER",
password="YOUR DB PASSWORD",
dsn="localhost:1521/...",
)
memory_store_id = "T_IMAGE_GUIDE"
memory = OracleAgentMemory(
connection=db_pool,
embedder=embedder,
llm=vision_llm,
memory_extraction_config=MemoryExtractionConfig(
extract_memories=False,
extraction_mode=MemoryExtractionMode.BACKGROUND,
),
image_input_limit_config=ImageInputLimitConfig(
max_raw_image_bytes=10 * 1024 * 1024,
max_images_per_llm_request=20,
max_total_raw_image_bytes_per_llm_request=50 * 1024 * 1024,
),
schema_policy=SchemaPolicy.CREATE_IF_NECESSARY,
memory_store_id=memory_store_id,
)
image_bytes = Path("sample.png").read_bytes()
##Store a standalone image
image_id = memory.add_image(
image_bytes,
mime_type=ImageMimeType.PNG,
user_id="customer_123",
description="A red sample image used by the image support guide.",
metadata={"source": "profile-photo"},
)
images = memory.list_images(user_id="customer_123", limit=10)
stored_image = next(image for image in images if image.id == image_id)
print(stored_image.content)
##Add a message with an image dictionary
thread = memory.create_thread(
thread_id="image-support-thread",
user_id="customer_123",
memory_extraction_config=MemoryExtractionConfig(
extract_memories=False,
extraction_mode=MemoryExtractionMode.BACKGROUND,
),
)
message_id = thread.add_messages(
[
{
"role": "user",
"content": [
{"type": "text", "text": "Please remember what is in this image."},
{
"type": "image",
"bytes": image_bytes,
"mime_type": "image/png",
},
],
}
]
)[0]
##Generate an attached image description
memory.wait_for_memory_extraction()
stored_message = thread.get_message(message_id)
attached_image = next(
part for part in stored_message.content if isinstance(part, ImageContent)
)
print(attached_image.description)
##Add typed multimodal message content
typed_message_id = thread.add_messages(
[
Message(
role="user",
content=[
TextContent(text="This message uses the typed content API."),
ImageContent(
bytes=image_bytes,
mime_type=ImageMimeType.PNG,
description="A red square on a white background.",
),
],
)
]
)[0]
print(typed_message_id)
##Search for images
standalone_matches = memory.search(
"red sample image",
user_id="customer_123",
record_types=["image"],
max_results=5,
)
for match in standalone_matches:
print(match.record.id, match.content)
attached_matches = thread.search(
"red square on a white background",
record_types=["image"],
max_results=5,
)
for match in attached_matches:
print(match.record.message_id, match.record.id, match.content)
##Retrieve standalone image bytes
#Raw bytes are omitted by default. To load them, use the image ID returned by
#the search result and pass the same user, agent, or thread ID used when
#adding it.
standalone_match = next(
match for match in standalone_matches if match.record.id == image_id
)
loaded_image = memory.list_images(
image_id=standalone_match.record.id,
user_id="customer_123",
include_bytes=True,
)[0]
##Retrieve attached image bytes
#get_message() omits image bytes unless included_image_ids selects them.
attached_match = next(
match
for match in attached_matches
if match.record.message_id == typed_message_id
)
message_with_bytes = thread.get_message(
attached_match.record.message_id,
included_image_ids=[attached_match.record.id],
)
hydrated_image = next(
part for part in message_with_bytes.content if isinstance(part, ImageContent)
)
##Extract memories from an image description
caption_thread = memory.create_thread(
thread_id="caption-extraction-thread",
user_id="customer_123",
memory_extraction_config=MemoryExtractionConfig(
extract_memories=True,
extraction_mode=MemoryExtractionMode.INLINE,
memory_extraction_frequency=1,
memory_extraction_image_context=MemoryExtractionImageContext.CAPTION,
),
)
caption_thread.add_messages(
[
Message(
role="user",
content=[
TextContent(text="Remember the color and shape in this image."),
ImageContent(
bytes=image_bytes,
mime_type=ImageMimeType.PNG,
description="A red square on a white background.",
),
],
)
]
)
##Update and delete a standalone image
memory.update_image(
image_id,
description="A red square used by the image support guide.",
metadata={"source": "reviewed-profile-photo"},
)
updated_image = memory.list_images(
image_id=image_id,
user_id="customer_123",
)[0]
print(updated_image.content)
print(updated_image.metadata)
deleted = memory.delete_image(image_id)
print(deleted)