링크된 메모리 그래프 생성 및 검색
메모리 링크는 기록 레코드를 보존하면서 관련 메모리 유사 레코드를 연결합니다. 새 메모리가 이전 메모리를 대체하거나 세분화할 때 또는 두 메모리가 서로 지원, 복제 또는 모순될 때 사용합니다. 자동 메모리 링크는 자동 메모리 추출의 일부로 이러한 링크를 만듭니다. 응용 프로그램이 이미 이러한 관계를 알고 있을 때는 수동 링크를 계속 사용할 수 있습니다.
이 가이드는 피자 기본 설정의 작은 그래프를 자동으로 만들고, 필요할 때 링크를 명시적으로 관리하는 방법을 보여주고, 검색으로 그래프 컨텍스트를 검색합니다.
링크된 메모리 클라이언트 설정
일반 OracleAgentMemory 클라이언트를 만듭니다. 첫번째 실행 시 SDK가 메모리 관계 저장소 및 속성 그래프를 포함하여 모든 관리 데이터베이스 객체를 생성하도록 SchemaPolicy.CREATE_IF_NECESSARY를 사용합니다.
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"
추출 중 메모리를 자동으로 연결
대부분의 응용 프로그램의 경우 자동 추출을 사용으로 설정하고 SDK가 add_messages()에서 새 메모리를 추출할 때 링크를 식별하도록 합니다. 기본 링크 모드인 POST_EXTRACTION는 먼저 메모리를 추출하고, 각 새 메모리에 대해 제한된 기존 후보 세트를 검색하고, 하나의 추가 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."}]
)
예를 들어, 스레드에 사용자가 Thin Crust를 선호하는 메모리가 이미 포함되어 있는 경우 링커는 새 Sourdough 기본 설정을 추출하고 이전 메모리에 대한 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는 링크된 각 메모리에 대해 사용 가능한 가장 짧은 경로를 사용하여 트리의 형식을 지정합니다. 순회는 경로에서 반복되는 메모리를 방지하고 대체 경로가 동일한 메모리에 도달할 때 하나의 결정적 최단 경로를 선택합니다.
프롬프트에 대한 그래프 컨텍스트 형식 지정
각 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))