Vector Search 소개 및 langchain 통합 가이드.

Amazon MemoryDB란?

MemoryDB는 널리 사용되는 오픈 소스 데이터 스토어인 Redis OSS와 호환되며, 오늘날 이미 사용 중인 유연하고 친숙한 Redis OSS 데이터 구조, API, 명령을 그대로 사용해 애플리케이션을 빠르게 구축할 수 있게 해줍니다. MemoryDB에서는 모든 데이터가 메모리에 저장되어 마이크로초 대의 읽기 지연, 한 자리 밀리초 대의 쓰기 지연, 그리고 높은 처리량을 달성할 수 있습니다. 또한 MemoryDB는 Multi-AZ transactional log를 사용해 여러 Availability Zones(AZs)에 걸쳐 데이터를 내구성 있게 저장하여 빠른 장애 조치, 데이터베이스 복구, 노드 재시작을 가능하게 합니다. MemoryDB의 Vector search는 MemoryDB의 기능을 확장합니다. Vector search는 기존의 MemoryDB 기능과 함께 사용할 수 있습니다. Vector search를 사용하지 않는 애플리케이션은 그 존재에 의해 영향을 받지 않습니다. Vector search는 MemoryDB가 제공되는 모든 리전에서 사용할 수 있습니다. 기존의 MemoryDB 데이터 또는 Redis OSS API를 사용해 retrieval-augmented generation, 이상 탐지, 문서 검색, 실시간 추천과 같은 머신 러닝 및 생성형 AI 활용 사례를 구축할 수 있습니다.
  • Redis hashes 및 JSON의 여러 필드 인덱싱
  • Vector similarity search (HNSW(ANN) 또는 FLAT(KNN) 사용)
  • Vector Range Search(예: 쿼리 벡터의 반경 내 모든 벡터 찾기)
  • 성능 저하 없이 점진적 인덱싱

설정하기

Redis Python client 설치하기

Redis-py는 MemoryDB에 연결하는 데 사용할 수 있는 python client입니다.
pip install -qU  redis langchain-aws
from langchain_aws.embeddings import BedrockEmbeddings

embeddings = BedrockEmbeddings()

MemoryDB 연결

유효한 Redis URL 스키마는 다음과 같습니다:
  1. redis:// - Redis 클러스터에 비암호화 연결
  2. rediss:// - TLS 암호화를 사용한 Redis 클러스터 연결
추가 연결 매개변수에 대한 자세한 내용은 redis-py documentation에서 확인할 수 있습니다.

샘플 데이터

먼저 Redis vector store의 다양한 속성을 시연하기 위해 일부 샘플 데이터를 설명합니다.
metadata = [
    {
        "user": "john",
        "age": 18,
        "job": "engineer",
        "credit_score": "high",
    },
    {
        "user": "derrick",
        "age": 45,
        "job": "doctor",
        "credit_score": "low",
    },
    {
        "user": "nancy",
        "age": 94,
        "job": "doctor",
        "credit_score": "high",
    },
    {
        "user": "tyler",
        "age": 100,
        "job": "engineer",
        "credit_score": "high",
    },
    {
        "user": "joe",
        "age": 35,
        "job": "dentist",
        "credit_score": "medium",
    },
]
texts = ["foo", "foo", "foo", "bar", "bar"]
index_name = "users"

MemoryDB vector store 생성하기

InMemoryVectorStore 인스턴스는 아래 방법으로 초기화할 수 있습니다
  • InMemoryVectorStore.__init__ - 직접 초기화
  • InMemoryVectorStore.from_documents - LangChain.docstore.Document 객체 리스트로부터 초기화
  • InMemoryVectorStore.from_texts - 텍스트 리스트(선택적으로 metadata 포함)로부터 초기화
  • InMemoryVectorStore.from_existing_index - 기존 MemoryDB 인덱스에서 초기화
from langchain_aws.vectorstores.inmemorydb import InMemoryVectorStore

vds = InMemoryVectorStore.from_texts(
    embeddings,
    redis_url="rediss://cluster_endpoint:6379/ssl=True ssl_cert_reqs=none",
)
vds.index_name
'users'

쿼리하기

사용 사례에 따라 InMemoryVectorStore 구현을 쿼리하는 여러 방법이 있습니다:
  • similarity_search: 주어진 벡터와 가장 유사한 벡터를 찾습니다.
  • similarity_search_with_score: 주어진 벡터와 가장 유사한 벡터를 찾고 벡터 거리를 반환합니다.
  • similarity_search_limit_score: 주어진 벡터와 가장 유사한 벡터를 찾고 결과 수를 score_threshold로 제한합니다.
  • similarity_search_with_relevance_scores: 주어진 벡터와 가장 유사한 벡터를 찾고 벡터 유사도를 반환합니다.
  • max_marginal_relevance_search: 다양성도 함께 최적화하면서 주어진 벡터와 가장 유사한 벡터를 찾습니다.
results = vds.similarity_search("foo")
print(results[0].page_content)
foo
# with scores (distances)
results = vds.similarity_search_with_score("foo", k=5)
for result in results:
    print(f"Content: {result[0].page_content} --- Score: {result[1]}")
Content: foo --- Score: 0.0
Content: foo --- Score: 0.0
Content: foo --- Score: 0.0
Content: bar --- Score: 0.1566
Content: bar --- Score: 0.1566
# limit the vector distance that can be returned
results = vds.similarity_search_with_score("foo", k=5, distance_threshold=0.1)
for result in results:
    print(f"Content: {result[0].page_content} --- Score: {result[1]}")
Content: foo --- Score: 0.0
Content: foo --- Score: 0.0
Content: foo --- Score: 0.0
# with scores
results = vds.similarity_search_with_relevance_scores("foo", k=5)
for result in results:
    print(f"Content: {result[0].page_content} --- Similiarity: {result[1]}")
Content: foo --- Similiarity: 1.0
Content: foo --- Similiarity: 1.0
Content: foo --- Similiarity: 1.0
Content: bar --- Similiarity: 0.8434
Content: bar --- Similiarity: 0.8434
# you can also add new documents as follows
new_document = ["baz"]
new_metadata = [{"user": "sam", "age": 50, "job": "janitor", "credit_score": "high"}]
# both the document and metadata must be lists
vds.add_texts(new_document, new_metadata)
['doc:users:b9c71d62a0a34241a37950b448dafd38']

Retriever로서의 MemoryDB

여기서는 vector store를 retriever로 사용하는 다양한 옵션을 살펴봅니다. 검색에는 세 가지 서로 다른 방법을 사용할 수 있습니다. 기본적으로 semantic similarity를 사용합니다.
query = "foo"
results = vds.similarity_search_with_score(query, k=3, return_metadata=True)

for result in results:
    print("Content:", result[0].page_content, " --- Score: ", result[1])
Content: foo  --- Score:  0.0
Content: foo  --- Score:  0.0
Content: foo  --- Score:  0.0
retriever = vds.as_retriever(search_type="similarity", search_kwargs={"k": 4})
docs = retriever.invoke(query)
docs
[Document(page_content='foo', metadata={'id': 'doc:users_modified:988ecca7574048e396756efc0e79aeca', 'user': 'john', 'job': 'engineer', 'credit_score': 'high', 'age': '18'}),
 Document(page_content='foo', metadata={'id': 'doc:users_modified:009b1afeb4084cc6bdef858c7a99b48e', 'user': 'derrick', 'job': 'doctor', 'credit_score': 'low', 'age': '45'}),
 Document(page_content='foo', metadata={'id': 'doc:users_modified:7087cee9be5b4eca93c30fbdd09a2731', 'user': 'nancy', 'job': 'doctor', 'credit_score': 'high', 'age': '94'}),
 Document(page_content='bar', metadata={'id': 'doc:users_modified:01ef6caac12b42c28ad870aefe574253', 'user': 'tyler', 'job': 'engineer', 'credit_score': 'high', 'age': '100'})]
또한 사용자가 vector distance를 지정할 수 있게 해주는 similarity_distance_threshold retriever도 있습니다.
retriever = vds.as_retriever(
    search_type="similarity_distance_threshold",
    search_kwargs={"k": 4, "distance_threshold": 0.1},
)
docs = retriever.invoke(query)
docs
[Document(page_content='foo', metadata={'id': 'doc:users_modified:988ecca7574048e396756efc0e79aeca', 'user': 'john', 'job': 'engineer', 'credit_score': 'high', 'age': '18'}),
 Document(page_content='foo', metadata={'id': 'doc:users_modified:009b1afeb4084cc6bdef858c7a99b48e', 'user': 'derrick', 'job': 'doctor', 'credit_score': 'low', 'age': '45'}),
 Document(page_content='foo', metadata={'id': 'doc:users_modified:7087cee9be5b4eca93c30fbdd09a2731', 'user': 'nancy', 'job': 'doctor', 'credit_score': 'high', 'age': '94'})]
마지막으로, similarity_score_threshold는 유사한 문서의 최소 점수를 사용자가 정의할 수 있게 해줍니다.
retriever = vds.as_retriever(
    search_type="similarity_score_threshold",
    search_kwargs={"score_threshold": 0.9, "k": 10},
)
retriever.invoke("foo")
[Document(page_content='foo', metadata={'id': 'doc:users_modified:988ecca7574048e396756efc0e79aeca', 'user': 'john', 'job': 'engineer', 'credit_score': 'high', 'age': '18'}),
 Document(page_content='foo', metadata={'id': 'doc:users_modified:009b1afeb4084cc6bdef858c7a99b48e', 'user': 'derrick', 'job': 'doctor', 'credit_score': 'low', 'age': '45'}),
 Document(page_content='foo', metadata={'id': 'doc:users_modified:7087cee9be5b4eca93c30fbdd09a2731', 'user': 'nancy', 'job': 'doctor', 'credit_score': 'high', 'age': '94'})]
retriever.invoke("foo")
[Document(page_content='foo', metadata={'id': 'doc:users:8f6b673b390647809d510112cde01a27', 'user': 'john', 'job': 'engineer', 'credit_score': 'high', 'age': '18'}),
 Document(page_content='bar', metadata={'id': 'doc:users:93521560735d42328b48c9c6f6418d6a', 'user': 'tyler', 'job': 'engineer', 'credit_score': 'high', 'age': '100'}),
 Document(page_content='foo', metadata={'id': 'doc:users:125ecd39d07845eabf1a699d44134a5b', 'user': 'nancy', 'job': 'doctor', 'credit_score': 'high', 'age': '94'}),
 Document(page_content='foo', metadata={'id': 'doc:users:d6200ab3764c466082fde3eaab972a2a', 'user': 'derrick', 'job': 'doctor', 'credit_score': 'low', 'age': '45'})]

인덱스 삭제

항목을 삭제하려면 해당 키로 지정해야 합니다.
# delete the indices too
InMemoryVectorStore.drop_index(
    index_name="users", delete_documents=True, redis_url="redis://localhost:6379"
)
InMemoryVectorStore.drop_index(
    index_name="users_modified",
    delete_documents=True,
    redis_url="redis://localhost:6379",
)
True

Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I