Uso de imágenes y mensajes multimodales
Los agentes a menudo necesitan recordar imágenes y texto. Las capturas de pantalla, documentos, gráficos y fotografías pueden contener detalles que la memoria de solo texto no puede conservar.
En esta guía, aprenderá a:
- agregar imágenes independientes y adjuntar imágenes a mensajes de thread;
- buscar ambos tipos de imágenes;
- recuperar los bytes originales de la imagen;
- Configurar la extracción automática de memoria para utilizar texto de mensaje, descripciones de imágenes o imágenes originales.
Indicación: Para la configuración del paquete, consulte Get Started with Agent Memory. Si necesita una instancia local de Oracle AI Database para este ejemplo, siga Ejecución local de Oracle AI Database.
Creación de un Cliente OracleAgentMemory
Cree el cliente OracleAgentMemory que utilizan los ejemplos de esta guía. Se conecta a Oracle AI Database, configura un grabador y un LLM con capacidad de visión y establece límites para la entrada de imágenes. Consulte Formatos de revisión, límites y manejo de datos cerca del final de esta guía para obtener una explicación de estos límites.
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()
El cliente define supports_vision=True cuando crea Llm. Defina esta opción solo cuando el modelo y el punto final de proveedor seleccionados acepten la entrada de imagen. Si lo omite, Llm comprueba los metadatos del modelo. Cuando no hay metadatos disponibles, Llm envía una pequeña imagen de prueba para determinar si el punto final acepta la entrada de imagen. La configuración de supports_vision=True omite ambas comprobaciones; no crea un modelo de solo texto capaz de procesar imágenes.
| Referencia de API: Llm | Configuración de límite de entrada de imagen | MemoryExtractionConfig |
Almacenar una imagen independiente
Utilice OracleAgentMemory.add_image() para agregar una imagen autónoma. Incluya al menos uno de los valores user_id, agent_id o thread_id. Debe proporcionar un identificador coincidente cuando llame más tarde a list_images() o search().
Para la búsqueda, el SDK representa una imagen con una descripción de texto. Con la búsqueda vectorial, incrusta esa descripción con el mismo incrustador de texto utilizado para la búsqueda solo de texto; no incrusta los bytes de imagen. Si transfiere description a add_image(), el SDK utiliza ese texto. Si lo omite, el LLM configurado con capacidad de visión genera la descripción automáticamente. En el siguiente ejemplo, se proporciona una descripción, por lo que al agregar la imagen no se realiza una solicitud de 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() devuelve imágenes autónomas que coinciden con los valores user_id, agent_id o thread_id proporcionados. Cada valor ImageRecord.content contiene la descripción de la imagen.
Para asociar una imagen independiente a un thread, llame a OracleThread.add_image(). Este método utiliza el ID de thread y cualquier ID de usuario o agente almacenado en el thread, por lo que no los vuelve a transferir.
| Referencia de API: OracleAgentMemory | OracleThread | Registro de imagen |
Almacenamiento de una imagen como parte de un mensaje
Almacenar una imagen como parte de un mensaje de thread cuando proporciona contexto para un turno de conversación y no es necesario gestionarla por separado.
Un mensaje de solo texto puede utilizar una cadena para content. Para un mensaje que contiene texto e imágenes, transfiera una lista ordenada de partes de contenido. El SDK conserva ese orden cuando almacena y recupera el mensaje y cuando crea una petición de datos de extracción de memoria.
En formato de diccionario, cada parte del contenido tiene un valor type:
- Una parte de texto es
{"type": "text", "text": "..."}. - Una parte de imagen contiene
"type": "image",bytessin formato ymime_type. También puede contenerdescription.
Los valores mime_type soportados son "image/png", "image/jpeg" y "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]
El diccionario de imágenes de este ejemplo omite description, por lo que el SDK genera uno con el LLM configurado con capacidad de visión.
El cliente utiliza BACKGROUND, por lo que el SDK almacena el mensaje, genera la descripción de las colas y devuelve antes de que finalice la generación. Utilice INLINE cuando la descripción debe estar lista antes de que vuelva add_messages().
Llame a wait_for_memory_extraction() antes de leer o buscar la descripción generada. El método espera las tareas de descripción de imágenes y extracción de memoria en cola de este cliente.
Después de que la generación se realice correctamente, get_message() devuelve la descripción en el campo ImageContent.description de la parte de imagen:
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 generación falla o la cola rechaza la tarea, la imagen permanece almacenada sin una descripción. Marque ImageContent.description antes de utilizar el texto generado.
También puede crear el mensaje con objetos TextContent y ImageContent en lugar de diccionarios. Ambos formularios almacenan el mismo mensaje.
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)
Dado que este ImageContent incluye una descripción, el SDK no envía la imagen a un LLM para la generación de descripciones.
| Referencia de API: Llm | MemoryExtractionConfig | Mensajes y contenido del mensaje | OracleThread |
Buscar imágenes
Defina record_types=["image"] para buscar descripciones de imágenes. Esto busca imágenes independientes e imágenes adjuntas a los mensajes. Cada resultado es un ImageRecord sin los bytes de imagen originales.
Puede proporcionar la descripción usted mismo o dejar que el LLM configurado con capacidad de visión lo genere. El SDK almacena y busca las descripciones generadas de la misma forma que las descripciones proporcionadas.
Con la estrategia VECTOR por defecto que se utiliza en esta guía, el SDK divide una descripción de imagen en fragmentos cuando es necesario y embebebe esos fragmentos con el Embedder configurado. Embebe la consulta con el mismo embebedor y compara los vectores resultantes. Este proceso es el mismo para las imágenes independientes y las imágenes asociadas a los mensajes.
Otras estrategias de búsqueda procesan las descripciones de manera diferente. KEYWORD busca el texto de descripción almacenado sin crear incrustaciones. HYBRID combina la coincidencia de texto con los vectores producidos por su OracleDBEmbedder configurado.
En el siguiente ejemplo, se buscan ambas imágenes. Cada resultado incluye el ID de imagen. Una imagen adjunta también incluye el ID de su mensaje principal.
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)
Las siguientes secciones de recuperación utilizan estos ID para cargar los bytes originales.
Con record_types=["message"], la búsqueda examina solo el texto del mensaje. No busca descripciones de imágenes asociadas a mensajes; utilice record_types=["image"] para esas descripciones.
| Referencia de API: OracleAgentMemory | OracleThread | Resultado de búsqueda de Oracle | Embebedor |
Recuperar bytes de imagen independientes
Por defecto, list_images() devuelve metadatos y descripciones de imágenes, pero no los bytes almacenados. Para recuperar los bytes, defina include_bytes=True y proporcione el ID de imagen del resultado de búsqueda junto con un valor user_id, agent_id o thread_id coincidente. Estos filtros limitan la solicitud a una imagen.
#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]
| Referencia de API: OracleAgentMemory | Registro de imagen |
Recuperación de bytes de una imagen asociada a un mensaje
Por defecto, las partes de imagen devueltas por get_message() y get_messages() no incluyen sus bytes. Para recuperar una imagen, transfiera su ID del resultado de búsqueda a get_message(..., included_image_ids=[...]). El message_id de los resultados de búsqueda identifica el mensaje que se va a recuperar.
#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)
)
| Referencia de API: Mensajes y contenido de mensajes | OracleThread |
Gestionar imágenes asociadas a mensajes
Una imagen adjunta pertenece a su mensaje principal. Para reemplazar o eliminar una imagen adjunta, llame a update_message() con nuevo contenido de mensaje. Al suprimir el mensaje, también se suprimen todas las imágenes asociadas a él. delete_image() solo suprime las imágenes independientes. No puede actualizar el TTL de una imagen adjunta directamente.
Cuando se agrega una imagen adjunta, recibe el tiempo de caducidad del mensaje principal. Si update_message() hace que el mensaje caduque antes, el SDK también acorta el tiempo de caducidad de la imagen. Al ampliar o borrar el tiempo de caducidad del mensaje con ttl_days=None, no se amplía ni borra el tiempo de caducidad ya almacenado para la imagen. Las búsquedas y las lecturas excluyen la imagen una vez que la imagen o su mensaje principal han caducado.
| Referencia de API: Mensajes y contenido de mensajes | OracleThread |
Seleccionar lo que ve la extracción automática de memoria
Defina MemoryExtractionConfig.memory_extraction_image_context para controlar qué partes de un mensaje que contienen imágenes recibe el LLM de extracción de memoria. Este valor sólo cambia la petición de datos de extracción. No cambia el mensaje almacenado ni genera una descripción de imagen faltante.
Contexto de imagen para la extracción automática de memoria
| Valor | Lo que recibe el LLM de extracción | Selecciónelo cuando |
|---|---|---|
DISABLED |
Sólo las partes de texto del mensaje. | La extracción debe ignorar las imágenes. Éste es el estado por defecto. |
CAPTION |
Partes de texto y descripciones de imágenes, en su orden original. | Las descripciones contienen la información visual necesaria para la extracción o el proveedor de LLM no debe recibir bytes de imagen. |
IMAGE |
Partes de texto y las imágenes originales, en su orden original. | Los recuerdos dependen de detalles visuales que las descripciones no contienen. El LLM y su punto final deben aceptar la entrada de imagen. |
En el siguiente ejemplo se utiliza CAPTION. Con memory_extraction_frequency=1, la extracción se ejecuta después del primer mensaje. Con MemoryExtractionMode.INLINE, la extracción finaliza antes de que se devuelva add_messages().
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.",
),
],
)
]
)
Para enviar la imagen original en su lugar, sustituya MemoryExtractionImageContext.CAPTION por MemoryExtractionImageContext.IMAGE. El LLM configurado en OracleAgentMemory debe aceptar la entrada de imagen.
En el modo CAPTION, cada imagen enviada para extracción debe tener una descripción que no esté vacía. Proporcione description al agregar cada imagen o configure un LLM con capacidad de visión para generar las descripciones. Con MemoryExtractionMode.BACKGROUND, el SDK pone en cola la generación de descripciones antes de la extracción de memoria para el mismo thread. Si una imagen aún no tiene ninguna descripción cuando comienza la extracción, el SDK rechaza la solicitud.
No utilice MemoryExtractionImageContext.MEMORY. Este valor está reservado para uso futuro y el SDK lo rechaza.
| Referencia de API: MemoryExtractionImageContext | MemoryExtractionConfig |
Actualización o supresión de una imagen independiente
Después de agregar una imagen independiente, utilice update_image() para cambiar su descripción, metadatos o bytes. La descripción se almacena en ImageRecord.content y se indexa para la búsqueda. Transfiera una cadena para sustituirla o transfiera None para generar una sustitución con el LLM de visión configurado. Si omite description, la descripción existente permanece sin cambios.
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)
El ejemplo recupera la imagen después de update_image() para verificar la nueva descripción y los metadatos. A continuación, transfiere image_id a delete_image() y comprueba que se haya suprimido una imagen.
Para sustituir los bytes, transfiera image y mime_type juntos. En la misma llamada, puede mantener la descripción actual, proporcionar una nueva o solicitar una sustitución generada por description=None. A diferencia de una imagen asociada a un mensaje, una imagen independiente puede tener sus propios metadatos, registros de hora y TTL.
| Referencia de API: OracleAgentMemory | Resultado de búsqueda de Oracle |
Revisión de formatos, límites y gestión de datos
Antes de almacenar una imagen, el SDK descodifica sus bytes y verifica el formato. Si transfiere mime_type, el formato descodificado debe coincidir con él. El SDK acepta imágenes PNG, JPEG y WebP, pero rechaza PNG animado y WebP animado. No puede desactivar esta validación.
Por defecto, una imagen raw puede tener hasta 10 MiB. Una sola solicitud de LLM puede contener hasta 100 imágenes y 100 MiB de datos de imagen. Utilice ImageInputLimitConfig para reducir estos límites para su despliegue o aumentarlos hasta los valores máximos documentados. El cliente al inicio de esta guía permite 10 MiB por imagen, 20 imágenes por solicitud y 50 MiB de datos de imagen por solicitud.
Oracle AI Agent Memory almacena bytes de imagen en Oracle AI Database. La generación de una descripción envía esos bytes al proveedor de LLM configurado. La extracción de memoria también envía los bytes en modo IMAGE. En el modo CAPTION, la extracción de memoria envía descripciones de imagen en su lugar. Una descripción puede revelar información de la imagen original. Revise las Consideraciones de seguridad antes de enviar imágenes confidenciales a un LLM para la generación de descripciones o la extracción de memoria.
Conclusión
En esta guía aprendimos a agregar imágenes independientes, adjuntar imágenes a mensajes, recuperar bytes de imágenes, buscar descripciones de imágenes y configurar la extracción automática de memoria para usar texto de mensaje, descripciones de imágenes o imágenes originales.
→ Después de haber aprendido a usar imágenes y mensajes multimodales, ahora puede proceder a Utilizar el tiempo de vida para mensajes y recuerdos.
Código Completo
El ejemplo completo se incluye en esta guía para que pueda copiar y ejecutar.
#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)