Bilder und multimodale Nachrichten verwenden

Agenten müssen sich oft Bilder und Texte merken. Screenshots, Dokumente, Diagramme und Fotos können Details enthalten, die der reine Textspeicher nicht beibehalten kann.

In diesem Handbuch erfahren Sie, wie Sie:

Hinweis: Informationen zum Packagesetup finden Sie unter Erste Schritte mit Agent-Speicher. Wenn Sie für dieses Beispiel eine lokale Oracle AI Database benötigen, folgen Sie Oracle AI Database lokal ausführen.

OracleAgentMemory-Client erstellen

Erstellen Sie den OracleAgentMemory-Client, der von den Beispielen in dieser Dokumentation verwendet wird. Sie stellt eine Verbindung zu Oracle AI Database her, konfiguriert einen Einbettungs- und ein visionäres LLM und legt Grenzen für die Bildeingabe fest. Eine Erläuterung dieser Grenzwerte finden Sie am Ende dieser Dokumentation unter Überprüfungsformate, Grenzwerte und Datenverarbeitung.

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()

Der Client legt supports_vision=True fest, wenn er Llm erstellt. Legen Sie diese Option nur fest, wenn das ausgewählte Modell und der Providerendpunkt die Bildeingabe akzeptieren. Wenn Sie es auslassen, prüft Llm die Modellmetadaten. Wenn keine Metadaten verfügbar sind, sendet Llm ein kleines Testimage, um zu bestimmen, ob der Endpunkt die Bildeingabe akzeptiert. Wenn Sie supports_vision=True festlegen, werden beide Prüfungen übersprungen. Es wird kein Nur-Text-Modell erstellt, das Bilder verarbeiten kann.

API-Referenz: Llm ImageInputLimitConfig MemoryExtractionConfig

Standalone-Image speichern

Mit OracleAgentMemory.add_image() können Sie ein Standalone-Image hinzufügen. Nehmen Sie mindestens einen der Werte user_id, agent_id oder thread_id auf. Sie müssen eine übereinstimmende ID angeben, wenn Sie später list_images() oder search() aufrufen.

Für die Suche stellt das SDK ein Bild mit einer Textbeschreibung dar. Bei der Vektorsuche wird diese Beschreibung mit demselben Text-Embedder eingebettet, der für die Nur-Text-Suche verwendet wird. Die Bildbyte werden nicht eingebettet. Wenn Sie description an add_image() übergeben, verwendet das SDK diesen Text. Wenn Sie es auslassen, generiert das konfigurierte visionsfähige LLM die Beschreibung automatisch. Das folgende Beispiel enthält eine Beschreibung. Wenn Sie das Bild hinzufügen, wird keine LLM-Anforderung erstellt.

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() gibt Standalone-Images zurück, die den angegebenen Werten für user_id, agent_id oder thread_id entsprechen. Jeder ImageRecord.content-Wert enthält die Bildbeschreibung.

Um ein Standalone-Image mit einem Thread zu verknüpfen, rufen Sie OracleThread.add_image() auf. Bei dieser Methode werden die Thread-ID und alle im Thread gespeicherten Benutzer- oder Agent-IDs verwendet, sodass Sie sie nicht erneut übergeben.

API-Referenz: OracleAgentMemory OracleThread Bilddatensatz

Bild als Teil einer Nachricht speichern

Speichern Sie ein Bild als Teil einer Threadnachricht, wenn es Kontext für eine Konversationsumschaltung bereitstellt und nicht separat verwaltet werden muss.

Eine Nur-Text-Nachricht kann eine Zeichenfolge für content verwenden. Für eine Nachricht, die Text und Bilder enthält, übergeben Sie eine geordnete Liste mit Inhaltsteilen. Das SDK behält diese Reihenfolge bei, wenn es die Nachricht speichert und abruft und wenn es eine Speicherextraktions-Eingabeaufforderung erstellt.

Im Dictionary-Formular hat jeder Inhaltsteil eine type:

Die unterstützten mime_type-Werte sind "image/png", "image/jpeg" und "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]

Das Image Dictionary in diesem Beispiel lässt description aus, sodass das SDK eines mit dem konfigurierten vision-fähigen LLM generiert.

Der Client verwendet BACKGROUND. Daher speichert das SDK die Nachricht, die Generierung der Queuesbeschreibung und die Rückgabe, bevor die Generierung abgeschlossen ist. Verwenden Sie INLINE, wenn die Beschreibung bereit sein muss, bevor add_messages() zurückgibt.

Rufen Sie wait_for_memory_extraction() auf, bevor Sie die generierte Beschreibung lesen oder suchen. Die Methode wartet auf die Bildbeschreibungs- und Speicherextraktionsaufgaben, die von diesem Client in die Queue gestellt werden.

Nachdem die Generierung erfolgreich war, gibt get_message() die Beschreibung im Feld ImageContent.description des Imageteils zurück:

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)

Wenn die Generierung fehlschlägt oder die Warteschlange die Aufgabe ablehnt, bleibt das Bild ohne Beschreibung gespeichert. Prüfen Sie ImageContent.description, bevor Sie den generierten Text verwenden.

Sie können die Nachricht auch mit TextContent- und ImageContent-Objekten anstelle von Wörterbüchern erstellen. Beide Formulare speichern dieselbe Nachricht.

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)

Da diese ImageContent eine Beschreibung enthält, sendet das SDK das Image nicht zur Generierung von Beschreibungen an ein LLM.

API-Referenz: Llm MemoryExtractionConfig Nachrichten und Nachrichteninhalte OracleThread

Nach Bildern suchen

Legen Sie record_types=["image"] fest, um Bildbeschreibungen zu suchen. Dabei werden sowohl Standalone-Bilder als auch Bilder durchsucht, die an Nachrichten angehängt sind. Jedes Ergebnis ist ein ImageRecord ohne die ursprünglichen Bildbyte.

Sie können die Beschreibung selbst angeben oder vom konfigurierten visionsfähigen LLM generieren lassen. Das SDK speichert und durchsucht generierte Beschreibungen auf die gleiche Weise wie die angegebenen Beschreibungen.

Wenn in dieser Dokumentation die Standardstrategie VECTOR verwendet wird, teilt das SDK bei Bedarf eine Imagebeschreibung in Chunks auf und bettet diese Chunks mit dem konfigurierten Embedder ein. Die Abfrage wird mit demselben Embedder eingebettet und die resultierenden Vektoren verglichen. Dieser Prozess ist für Standalone-Bilder und Bilder, die an Nachrichten angehängt sind, identisch.

Andere Suchstrategien verarbeiten Beschreibungen unterschiedlich. KEYWORD durchsucht den gespeicherten Beschreibungstext, ohne Einbettungen zu erstellen. HYBRID kombiniert Textabgleich mit Vektoren, die von der konfigurierten OracleDBEmbedder erzeugt werden.

Im folgenden Beispiel wird nach beiden Bildern gesucht. Jedes Ergebnis enthält die Bildkennung. Ein angehängtes Bild enthält auch die ID der übergeordneten Nachricht.

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)

Die folgenden Abrufabschnitte verwenden diese IDs, um die ursprünglichen Byte zu laden.

Bei record_types=["message"] prüft search nur Nachrichtentext. Es werden keine Beschreibungen von Bildern gesucht, die an Nachrichten angehängt sind. Verwenden Sie record_types=["image"] für diese Beschreibungen.

API-Referenz: OracleAgentMemory OracleThread OracleSearchResult Einbettung

Standalone-Bildbyte abrufen

Standardmäßig gibt list_images() Bildmetadaten und -beschreibungen zurück, nicht jedoch die gespeicherten Byte. Um die Byte abzurufen, legen Sie include_bytes=True fest, und geben Sie die Bild-ID aus dem Suchergebnis zusammen mit einem übereinstimmenden Wert für user_id, agent_id oder thread_id an. Diese Filter begrenzen die Anforderung auf ein Bild.

#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]
API-Referenz: OracleAgentMemory Bilddatensatz

Byte für ein an eine Nachricht angehängtes Bild abrufen

Standardmäßig enthalten die von get_message() und get_messages() zurückgegebenen Imageteile keine Byte. Um ein Bild abzurufen, übergeben Sie seine ID aus dem Suchergebnis an get_message(..., included_image_ids=[...]). Das Suchergebnis message_id gibt die abzurufende Nachricht an.

#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)
)
API-Referenz: Nachrichten und Nachrichteninhalte OracleThread

Mit Nachrichten verknüpfte Bilder verwalten

Ein angehängtes Bild gehört zu seiner übergeordneten Nachricht. Um ein angehängtes Bild zu ersetzen oder zu entfernen, rufen Sie update_message() mit neuem Nachrichteninhalt auf. Wenn Sie die Nachricht löschen, wird auch jedes daran angehängte Bild gelöscht. delete_image() löscht nur Standalone-Images. Sie können die TTL eines angehängten Bildes nicht direkt aktualisieren.

Wenn Sie ein angehängtes Bild hinzufügen, erhält es die Ablaufzeit der übergeordneten Nachricht. Wenn update_message() die Nachricht früher abläuft, verkürzt das SDK auch die Ablaufzeit des Images. Wenn Sie die Ablaufzeit der Nachricht erweitern oder sie mit ttl_days=None löschen, wird die bereits für das Image gespeicherte Ablaufzeit weder erweitert noch gelöscht. Lese- und Suchvorgänge schließen das Bild aus, nachdem das Bild oder die übergeordnete Nachricht abgelaufen ist.

API-Referenz: Nachrichten und Nachrichteninhalte OracleThread

Sees für automatische Speicherextraktion auswählen

Legen Sie MemoryExtractionConfig.memory_extraction_image_context fest, um zu steuern, welche Teile einer Nachricht Bilder enthalten, die das Speicherextraktions-LLM empfängt. Diese Einstellung ändert nur den Extrahierungs-Prompt. Die gespeicherte Nachricht wird nicht geändert oder eine fehlende Bildbeschreibung generiert.

Bildkontext für automatische Speicherextraktion

Wert Was das Extraktions-LLM erhält Wählen Sie diese Option aus, wenn
DISABLED Nur die Textteile der Nachricht. Die Extraktion sollte Bilder ignorieren. Dies ist die standardmäßige Einstellung.
CAPTION Textteile und Bildbeschreibungen in ihrer ursprünglichen Reihenfolge. Die Beschreibungen enthalten die visuellen Informationen, die für die Extraktion benötigt werden, oder der LLM-Provider darf keine Bildbyte empfangen.
IMAGE Textteile und Originalbilder in ihrer ursprünglichen Reihenfolge. Die Erinnerungen hängen von visuellen Details ab, die in den Beschreibungen nicht enthalten sind. Das LLM und sein Endpunkt müssen Bildeingaben akzeptieren.

Im folgenden Beispiel wird CAPTION verwendet. Bei memory_extraction_frequency=1 wird die Extraktion nach der ersten Nachricht ausgeführt. Bei MemoryExtractionMode.INLINE wird die Extraktion abgeschlossen, bevor add_messages() zurückgibt.

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.",
                ),
            ],
        )
    ]
)

Um stattdessen das Originalbild zu senden, ersetzen Sie MemoryExtractionImageContext.CAPTION durch MemoryExtractionImageContext.IMAGE. Das in OracleAgentMemory konfigurierte LLM muss die Bildeingabe akzeptieren.

Im CAPTION-Modus muss jedes zur Extraktion gesendete Image eine nicht leere Beschreibung aufweisen. Geben Sie description an, wenn Sie jedes Bild hinzufügen, oder konfigurieren Sie ein visionfähiges LLM, um die Beschreibungen zu generieren. Bei MemoryExtractionMode.BACKGROUND generiert die SDK Queues-Beschreibung vor der Speicherextraktion für denselben Thread. Wenn ein Image zu Beginn der Extraktion noch keine Beschreibung hat, lehnt das SDK die Anforderung ab.

Verwenden Sie MemoryExtractionImageContext.MEMORY nicht. Dieser Wert ist für die zukünftige Verwendung reserviert, und das SDK lehnt ihn ab.

API-Referenz: MemoryExtractionImageContext MemoryExtractionConfig

Standalone-Image aktualisieren oder löschen

Nachdem Sie ein Standalone-Image hinzugefügt haben, verwenden Sie update_image(), um die Beschreibung, Metadaten oder Byte zu ändern. Die Beschreibung wird in ImageRecord.content gespeichert und für die Suche indiziert. Übergeben Sie eine Zeichenfolge, um sie zu ersetzen, oder übergeben Sie None, um einen Ersatz mit dem konfigurierten Vision-LLM zu generieren. Wenn Sie description weglassen, bleibt die vorhandene Beschreibung unverändert.

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)

Im Beispiel wird das Image nach update_image() abgerufen, um die neue Beschreibung und die neuen Metadaten zu prüfen. Anschließend wird image_id an delete_image() übergeben und geprüft, ob ein Image gelöscht wurde.

Um die Byte zu ersetzen, übergeben Sie image und mime_type zusammen. Im selben Aufruf können Sie die aktuelle Beschreibung beibehalten, eine neue angeben oder einen generierten Ersatz mit description=None anfordern. Im Gegensatz zu einem Image, das an eine Nachricht angehängt ist, kann ein Standalone-Image über eigene Metadaten, Zeitstempel und TTL verfügen.

API-Referenz: OracleAgentMemory OracleSearchResult

Formate, Limits und Datenverarbeitung prüfen

Bevor ein Image gespeichert wird, decodiert das SDK seine Byte und prüft das Format. Wenn Sie mime_type übergeben, muss das decodierte Format übereinstimmen. Das SDK akzeptiert PNG-, JPEG- und WebP-Bilder, lehnt jedoch animierte PNG- und animierte WebP-Dateien ab. Sie können diese Validierung nicht deaktivieren.

Standardmäßig kann ein Raw-Image bis zu 10 MiB betragen. Eine einzelne LLM-Anforderung kann bis zu 100 Bilder und 100 MiB Bilddaten enthalten. Verwenden Sie ImageInputLimitConfig, um diese Grenzwerte für Ihr Deployment zu senken oder sie auf die dokumentierten Höchstwerte anzuheben. Der Client am Anfang dieses Handbuchs ermöglicht 10 MiB pro Bild, 20 Bilder pro Anforderung und 50 MiB Bilddaten pro Anforderung.

Oracle AI Agent Memory speichert Imagebyte in Oracle AI Database. Beim Generieren einer Beschreibung werden diese Byte an den konfigurierten LLM-Provider gesendet. Die Speicherextraktion sendet die Byte auch im IMAGE-Modus. Im Modus CAPTION sendet die Speicherextraktion stattdessen Bildbeschreibungen. Eine Beschreibung kann Informationen aus dem Originalbild anzeigen. Prüfen Sie die Sicherheitsbetrachtungen, bevor Sie sensible Images zur Beschreibungsgenerierung oder Speicherextraktion an ein LLM senden.

Abschluss

In diesem Handbuch haben wir gelernt, wie Sie Standalone-Bilder hinzufügen, Bilder an Nachrichten anhängen, Bildbyte abrufen, Bildbeschreibungen suchen und die automatische Speicherextraktion so konfigurieren, dass Nachrichtentext, Bildbeschreibungen oder Originalbilder verwendet werden.

→ Nachdem Sie gelernt haben, wie Sie Bilder und multimodale Nachrichten verwenden, können Sie jetzt Time-to-Live für Nachrichten und Erinnerungen verwenden.

Vollständiger Code

Das vollständige Beispiel ist in diesem Handbuch enthalten, das Sie kopieren und ausführen können.

#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)