リンクされたメモリーグラフの作成および検索

メモリー・リンクは、履歴レコードを保持しながら、関連するメモリーに似たレコードを接続します。新しいメモリが古いメモリよりも優先または改良される場合、または2つのメモリが互いにサポート、複製、または矛盾する場合に使用します。自動メモリーリンクは、自動メモリー抽出の一部としてこれらのリンクを作成します。アプリケーションがすでにその関係を認識している場合、手動リンクは引き続き使用できます。

このガイドでは、ピザ・プリファレンスの小さなグラフを自動的に作成し、必要に応じてリンクを明示的に管理する方法を示し、検索を使用してグラフ・コンテキストを取得します。

リンクメモリークライアントの設定

通常のOracleAgentMemoryクライアントを作成します。最初の実行では、SchemaPolicy.CREATE_IF_NECESSARYを使用して、SDKがメモリー・リレーション・ストアおよびプロパティ・グラフを含むすべての管理対象データベース・オブジェクトを作成します。

import oracledb

from oracleagentmemory.core import (
    MemoryExtractionConfig,
    OracleAgentMemory,
    OracleSearchResultFormatConfig,
    SchemaPolicy,
)
from oracleagentmemory.core.embedders.embedder import Embedder


embedder = Embedder(model="YOUR_EMBEDDING_MODEL")
db_pool = oracledb.SessionPool(
    user="YOUR DB USER",
    password="YOUR DB PASSWORD",
    dsn="localhost:1521/...",
)
memory_store_id = "T_GRAPH_MEMORY"

memory = OracleAgentMemory(
    connection=db_pool,
    embedder=embedder,
    schema_policy=SchemaPolicy.CREATE_IF_NECESSARY,
    memory_store_id=memory_store_id,
    memory_extraction_config=MemoryExtractionConfig(extract_memories=False),
)
user_id = "graph-demo-user"

抽出中にメモリーを自動的にリンク

ほとんどのアプリケーションでは、自動抽出を有効にし、add_messages()から新しいメモリーを抽出する際にSDKでリンクを識別できるようにします。デフォルトのリンク・モードPOST_EXTRACTIONは、最初にメモリーを抽出し、新しいメモリーごとに既存の候補のバインドされたセットを取得し、1つの追加LLMリクエストを使用して、型付きリンクを作成するかどうかを決定します。これにより、リンク解像度に独自のコンテキストが与えられ、リンク品質が重要な場合に推奨されるモードになります。

クライアント・レベルまたはスレッド・レベルでMemoryExtractionConfigを使用してモードを構成します。次のスレッドは、追加されるメッセージごとに抽出され、抽出後にリンクを解決します。OracleAgentMemoryクライアントは、抽出LLMを使用して構成する必要もあります。

from oracleagentmemory.core import (
    MemoryExtractionConfig,
    MemoryLinkExtractionMode,
)

preference_thread = memory.create_thread(
    thread_id="pizza-preferences",
    user_id=user_id,
    memory_extraction_config=MemoryExtractionConfig(
        extract_memories=True,
        memory_extraction_frequency=1,
        memory_link_extraction_mode=MemoryLinkExtractionMode.POST_EXTRACTION,
    ),
)
preference_thread.add_messages(
    [{"role": "user", "content": "I now prefer sourdough pizza."}]
)

たとえば、スレッドにシン・クラストを優先するメモリーがすでに含まれている場合、リンカーは新しいサワーダフ・プリファレンスを抽出して、古いメモリーへのsupersedesリンクを作成できます。supersedes、refines、duplicatesなどのライフサイクル・リンク・タイプは、古いメモリーを履歴として保持し、それを無効としてマークします。グラフ検索では、リンクされたコンテキストとして戻すことができます。

DURING_EXTRACTIONは、メモリーを抽出するリクエストと同じリクエスト内のリンクを識別するように抽出LLMに要求します。これにより、追加のリンク解決リクエストが回避されます。その候補は抽出検索によって制限されます。自動リンクをオフにするには、memory_link_extraction_mode=MemoryLinkExtractionMode.DISABLEDを設定します。どちらの自動モードも、ストア内のすべての可能な関係を検出しようとしないため、記録する必要がある関係に明示的なリンクを使用します。

リンクされたメモリーの手動作成

通常どおりにメモリーを作成し、link_records()で接続します。リレーションは、そのソースがそのターゲットを指し示します。supersedes、refinesおよびduplicatesは、ターゲットを無効にします。supportsおよびcontradictsは、両方のエンドポイント・メモリーを有効なままにします。

リンク・タイプの選択

メモリー展開の場合、ソースは通常より新しいメモリーで、ターゲットは既存のメモリーです。その関係を説明するリンク タイプを選択します。

リンク・タイプ ソース・メモリーを使用する場合に使用します。 ターゲット・メモリーへの影響
supersedes ターゲットを現在の情報として置き換えます。たとえば、以前のプリファレンスが新しいプリファレンスに置き換えられます。 無効になりますが、履歴として引き続き使用できます。
refines 詳細または精度を追加しながら、ターゲットの情報を保持します。たとえば、特定のハイキング・プリファレンスによって一般的なものが絞り込まれます。 無効になりますが、履歴として引き続き使用できます。
duplicates ターゲットと同じ意味です。ソースが優先コピーです。 直接結果の重複を防ぐために無効になります。
supports ターゲットの証拠を提供します。たとえば、肉や魚を避けることは、菜食主義者の好みを支えます。 変更されないままです。
contradicts ターゲットと競合しますが、どのメモリーが正しいかをSDKで判断できません。 変更されないままです。

グラフ検索は、どちらの方向にも各リンクに従います。ターゲットからソースにトラバースすると、対応するリバース・ラベル(is_superseded_byやis_refined_byなど)が表示されます。

thin_crust_id = memory.add_memory(
    "The user's preferred pizza style is thin crust.",
    memory_id="pizza-thin-crust",
    memory_type="preference",
    user_id=user_id,
)
sourdough_id = memory.add_memory(
    "The user now prefers sourdough pizza.",
    memory_id="pizza-sourdough",
    memory_type="preference",
    user_id=user_id,
)
neapolitan_id = memory.add_memory(
    "The user's preferred pizza style is sourdough, especially Neapolitan sourdough.",
    memory_id="pizza-neapolitan-sourdough",
    memory_type="preference",
    user_id=user_id,
)

supersedes_link_id = memory.link_records(
    source_record_id=sourdough_id,
    source_record_type="preference",
    target_record_id=thin_crust_id,
    target_record_type="preference",
    relation_type="supersedes",
)
memory.link_records(
    source_record_id=neapolitan_id,
    source_record_type="preference",
    target_record_id=sourdough_id,
    target_record_type="preference",
    relation_type="refines",
)

検索は、結果の格納された方向を保持しながら、いずれかの方向のリンクに従います。

リンクの編集および削除

リレーションを更新するには、link_records()によって返されるリレーションIDを使用します。省略されたフィールドは変更されません。リレーション・タイプを更新するとエンドポイント・ステータスが再計算されるため、置換タイプにライフサイクル効果がない場合、以前のライフサイクル・リンクはメモリーを無効のままにしなくなります。

リレーションIDまたは完全な有向エンドポイント・タプル(ソースIDとタイプ、ターゲットIDとタイプ、リレーション・タイプ)によって、リレーションを削除します。削除では、影響を受けるエンドポイント・ステータスも再計算されます。

#Changing a lifecycle link recalculates the status of both endpoint memories.
memory.update_record_link(
    supersedes_link_id,
    relation_type="supports",
    metadata={"reviewed_by": "preference-service"},
)

#Delete by the stable relation ID.
memory.delete_record_link(relation_id=supersedes_link_id)

#Or delete by the complete directed relation triple.
memory.delete_record_link(
    source_record_id=neapolitan_id,
    source_record_type="preference",
    target_record_id=sourdough_id,
    target_record_type="preference",
    relation_type="refines",
)

リンクされたメモを検索

リンクされたコンテキストをすべてのダイレクト・メモリー結果にアタッチするには、num_hopsを設定します。0はデフォルトであり、直接の結果のみを返します。1から5までの値は、その多数のリンクに従います。max_linked_resultsは、すべてのホップで各直接結果にアタッチされたリンクされたメモリーの合計を制限します。デフォルトは100で、リンクされたコンテキストなしで直接の結果を返すには0に設定します。

スコープ・フィルタ、メタデータ・フィルタ、レコード・タイプ・フィルタおよび有効期限フィルタは、リンクされたメモリーに適用されます。include_invalid_resultsは、トップレベルの直接結果にのみ適用されます。リンクされたメモリーコンテキストには、そのオプションに関係なく、無効な履歴レコードを含めることができます。正のnum_hops値の場合、SDKは両方の方向のリンクに従い、次の項で説明する確定的最短パス・ツリーとしてリンクされたコンテキストを返します。

results = memory.search(
    "What pizza should I recommend?",
    user_id=user_id,
    max_results=5,
    num_hops=2,
    max_linked_results=20,
    include_invalid_results=False,
)

各結果には、再帰的なリンク結果ツリーが含まれます。SDKは、リンクされた各メモリーへの使用可能な最短パスを使用してツリーをフォーマットします。トラバーサルは、パス内の繰り返し記憶を防止し、代替パスが同じメモリーに到達したときに、決定論的な最短パスを1つ選択します。

プロンプトのグラフ・コンテキストの書式設定

各SearchResultは、format_content()を公開して、直接の結果とそのリンクされたメモリーを構造化されたプロンプト・テキストとしてレンダリングします。デフォルトでは、結果を生成した検索のinclude_invalid_results設定が使用されます。指定された書式設定構成は、明示的に設定されたオプションをオーバーライドします。無効なコンテンツが無効な場合、リンクされた無効なメモリーはコンテンツを省略します。有効なリンクされたメモリーにつながる無効なレコードは、ステータスとリンクコンテキストを保持します。無効なブランチは省略されます。

レンダリングされたツリーを調整するには、OracleSearchResultFormatConfigを使用します。この例では、タイムスタンプ、ロールおよびステータスを削除する際に、よりコンパクトなプロンプトに対して無効な履歴コンテンツが含まれています。メタデータ、スレッド、ユーザーまたはエージェントの識別子および推定関連性を含めることもできます。レンダリングされたすべてのリレーション・ラベルは、RecordRelationが格納された方向を保持していても、表示される親子方向を示します。

format_config = OracleSearchResultFormatConfig(
    include_invalid_results=True,
    show_timestamp=False,
    show_role=False,
    show_status=False,
)
for result in results:
    print(result.format_content(format_config))

完全コード

このガイドには、コピーして実行するための完全な例が含まれています。

#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 - Create and Search Linked Memory Graphs
#-------------------------------------------------------------------------

##Create a graph memory client

import oracledb

from oracleagentmemory.core import (
    MemoryExtractionConfig,
    OracleAgentMemory,
    OracleSearchResultFormatConfig,
    SchemaPolicy,
)
from oracleagentmemory.core.embedders.embedder import Embedder


embedder = Embedder(model="YOUR_EMBEDDING_MODEL")
db_pool = oracledb.SessionPool(
    user="YOUR DB USER",
    password="YOUR DB PASSWORD",
    dsn="localhost:1521/...",
)
memory_store_id = "T_GRAPH_MEMORY"

memory = OracleAgentMemory(
    connection=db_pool,
    embedder=embedder,
    schema_policy=SchemaPolicy.CREATE_IF_NECESSARY,
    memory_store_id=memory_store_id,
    memory_extraction_config=MemoryExtractionConfig(extract_memories=False),
)
user_id = "graph-demo-user"



##Create linked memories

thin_crust_id = memory.add_memory(
    "The user's preferred pizza style is thin crust.",
    memory_id="pizza-thin-crust",
    memory_type="preference",
    user_id=user_id,
)
sourdough_id = memory.add_memory(
    "The user now prefers sourdough pizza.",
    memory_id="pizza-sourdough",
    memory_type="preference",
    user_id=user_id,
)
neapolitan_id = memory.add_memory(
    "The user's preferred pizza style is sourdough, especially Neapolitan sourdough.",
    memory_id="pizza-neapolitan-sourdough",
    memory_type="preference",
    user_id=user_id,
)

supersedes_link_id = memory.link_records(
    source_record_id=sourdough_id,
    source_record_type="preference",
    target_record_id=thin_crust_id,
    target_record_type="preference",
    relation_type="supersedes",
)
memory.link_records(
    source_record_id=neapolitan_id,
    source_record_type="preference",
    target_record_id=sourdough_id,
    target_record_type="preference",
    relation_type="refines",
)



##Edit and delete links

#Changing a lifecycle link recalculates the status of both endpoint memories.
memory.update_record_link(
    supersedes_link_id,
    relation_type="supports",
    metadata={"reviewed_by": "preference-service"},
)

#Delete by the stable relation ID.
memory.delete_record_link(relation_id=supersedes_link_id)

#Or delete by the complete directed relation triple.
memory.delete_record_link(
    source_record_id=neapolitan_id,
    source_record_type="preference",
    target_record_id=sourdough_id,
    target_record_type="preference",
    relation_type="refines",
)


#Recreate the example links for the search example.
memory.link_records(sourdough_id, "preference", thin_crust_id, "preference", "supersedes")
memory.link_records(neapolitan_id, "preference", sourdough_id, "preference", "refines")



##Search linked memories

results = memory.search(
    "What pizza should I recommend?",
    user_id=user_id,
    max_results=5,
    num_hops=2,
    max_linked_results=20,
    include_invalid_results=False,
)



##Format graph context for a prompt

format_config = OracleSearchResultFormatConfig(
    include_invalid_results=True,
    show_timestamp=False,
    show_role=False,
    show_status=False,
)
for result in results:
    print(result.format_content(format_config))