Couchbase는 클라우드, 모바일, AI, 엣지 컴퓨팅 애플리케이션 전반에서 탁월한 유연성, 성능, 확장성, 비용 효율을 제공하는 수상 경력의 분산 NoSQL 클라우드 데이터베이스입니다. Couchbase는 개발자를 위한 코딩 지원과 애플리케이션을 위한 Vector Search로 AI를 적극적으로 지원합니다. Vector Search는 Couchbase의 Full Text Search Service (Search Service)의 일부입니다. 이 튜토리얼은 Couchbase에서 Vector Search를 사용하는 방법을 설명합니다. Couchbase Capella 또는 자체 관리형 Couchbase Server 중 하나와 함께 사용할 수 있습니다.

Setup

CouchbaseSearchVectorStore에 접근하려면 먼저 langchain-couchbase 파트너 패키지를 설치해야 합니다:
pip install -qU langchain-couchbase

Credentials

Couchbase website로 이동하여 새 연결을 생성하고, 데이터베이스 username과 password를 저장해 두세요:
import getpass

COUCHBASE_CONNECTION_STRING = getpass.getpass(
    "Enter the connection string for the Couchbase cluster: "
)
DB_USERNAME = getpass.getpass("Enter the username for the Couchbase cluster: ")
DB_PASSWORD = getpass.getpass("Enter the password for the Couchbase cluster: ")
Enter the connection string for the Couchbase cluster:  ········
Enter the username for the Couchbase cluster:  ········
Enter the password for the Couchbase cluster:  ········
모델 호출에 대한 최고 수준의 자동 트레이싱을 원한다면 아래의 주석을 제거하여 LangSmith API 키를 설정하세요:
os.environ["LANGSMITH_TRACING"] = "true"
# os.environ["LANGSMITH_API_KEY"] = getpass.getpass()

Initialization

인스턴스화하기 전에 연결을 생성해야 합니다.

Create Couchbase Connection Object

초기에 Couchbase 클러스터에 연결을 생성한 다음, 해당 cluster 객체를 Vector Store에 전달합니다. 여기서는 위에서 설정한 username과 password를 사용해 연결합니다. 클러스터에 대해 지원되는 다른 방식으로도 연결할 수 있습니다. Couchbase 클러스터 연결에 대한 자세한 내용은 documentation을 확인하세요.
from datetime import timedelta

from couchbase.auth import PasswordAuthenticator
from couchbase.cluster import Cluster
from couchbase.options import ClusterOptions

auth = PasswordAuthenticator(DB_USERNAME, DB_PASSWORD)
options = ClusterOptions(auth)
cluster = Cluster(COUCHBASE_CONNECTION_STRING, options)

# Wait until the cluster is ready for use.
cluster.wait_until_ready(timedelta(seconds=5))
이제 Vector Search에 사용할 Couchbase 클러스터의 bucket, scope, collection 이름을 설정합니다. 이 예제에서는 기본 scope 및 collections를 사용합니다.
BUCKET_NAME = "langchain_bucket"
SCOPE_NAME = "_default"
COLLECTION_NAME = "_default"
SEARCH_INDEX_NAME = "langchain-test-index"
Vector 필드를 지원하는 Search index를 생성하는 방법은 문서를 참조하세요.

Simple Instantiation

아래에서는 클러스터 정보와 Search index 이름으로 vector store 객체를 생성합니다.
# | output: false
# | echo: false
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
from langchain_couchbase.vectorstores import CouchbaseSearchVectorStore

vector_store = CouchbaseSearchVectorStore(
    cluster=cluster,
    bucket_name=BUCKET_NAME,
    scope_name=SCOPE_NAME,
    collection_name=COLLECTION_NAME,
    embedding=embeddings,
    index_name=SEARCH_INDEX_NAME,
)

Specify the Text & Embeddings Field

text_keyembedding_key 필드를 사용하여 문서의 text와 embeddings 필드를 선택적으로 지정할 수 있습니다.
vector_store_specific = CouchbaseSearchVectorStore(
    cluster=cluster,
    bucket_name=BUCKET_NAME,
    scope_name=SCOPE_NAME,
    collection_name=COLLECTION_NAME,
    embedding=embeddings,
    index_name=SEARCH_INDEX_NAME,
    text_key="text",
    embedding_key="embedding",
)

Manage vector store

vector store를 생성하면 다양한 항목을 추가 및 삭제하여 상호작용할 수 있습니다.

Add items to vector store

add_documents function을 사용하여 vector store에 항목을 추가할 수 있습니다.
from uuid import uuid4

from langchain_core.documents import Document

document_1 = Document(
    page_content="I had chocolate chip pancakes and scrambled eggs for breakfast this morning.",
    metadata={"source": "tweet"},
)

document_2 = Document(
    page_content="The weather forecast for tomorrow is cloudy and overcast, with a high of 62 degrees.",
    metadata={"source": "news"},
)

document_3 = Document(
    page_content="Building an exciting new project with LangChain - come check it out!",
    metadata={"source": "tweet"},
)

document_4 = Document(
    page_content="Robbers broke into the city bank and stole $1 million in cash.",
    metadata={"source": "news"},
)

document_5 = Document(
    page_content="Wow! That was an amazing movie. I can't wait to see it again.",
    metadata={"source": "tweet"},
)

document_6 = Document(
    page_content="Is the new iPhone worth the price? Read this review to find out.",
    metadata={"source": "website"},
)

document_7 = Document(
    page_content="The top 10 soccer players in the world right now.",
    metadata={"source": "website"},
)

document_8 = Document(
    page_content="LangGraph is the best framework for building stateful, agentic applications!",
    metadata={"source": "tweet"},
)

document_9 = Document(
    page_content="The stock market is down 500 points today due to fears of a recession.",
    metadata={"source": "news"},
)

document_10 = Document(
    page_content="I have a bad feeling I am going to get deleted :(",
    metadata={"source": "tweet"},
)

documents = [
    document_1,
    document_2,
    document_3,
    document_4,
    document_5,
    document_6,
    document_7,
    document_8,
    document_9,
    document_10,
]
uuids = [str(uuid4()) for _ in range(len(documents))]

vector_store.add_documents(documents=documents, ids=uuids)
['f125b836-f555-4449-98dc-cbda4e77ae3f',
 'a28fccde-fd32-4775-9ca8-6cdb22ca7031',
 'b1037c4b-947f-497f-84db-63a4def5080b',
 'c7082b74-b385-4c4b-bbe5-0740909c01db',
 'a7e31f62-13a5-4109-b881-8631aff7d46c',
 '9fcc2894-fdb1-41bd-9a93-8547747650f4',
 'a5b0632d-abaf-4802-99b3-df6b6c99be29',
 '0475592e-4b7f-425d-91fd-ac2459d48a36',
 '94c6db4e-ba07-43ff-aa96-3a5d577db43a',
 'd21c7feb-ad47-4e7d-84c5-785afb189160']

Delete items from vector store

vector_store.delete(ids=[uuids[-1]])
True

Query vector store

vector store가 생성되고 관련 문서가 추가되면, 체인 또는 에이전트 실행 중에 이를 질의하고 싶을 것입니다.

Query directly

간단한 similarity search는 다음과 같이 수행할 수 있습니다:
results = vector_store.similarity_search(
    "LangChain provides abstractions to make working with LLMs easy",
    k=2,
)
for res in results:
    print(f"* {res.page_content} [{res.metadata}]")
* Building an exciting new project with LangChain - come check it out! [{'source': 'tweet'}]
* LangGraph is the best framework for building stateful, agentic applications! [{'source': 'tweet'}]

Similarity search with Score

similarity_search_with_score method를 호출하여 결과의 score도 가져올 수 있습니다.
results = vector_store.similarity_search_with_score("Will it be hot tomorrow?", k=1)
for res, score in results:
    print(f"* [SIM={score:3f}] {res.page_content} [{res.metadata}]")
* [SIM=0.553112] The weather forecast for tomorrow is cloudy and overcast, with a high of 62 degrees. [{'source': 'news'}]

Filtering Results

Couchbase Search service에서 지원하는 문서의 text 또는 metadata에 대한 filter를 지정하여 검색 결과를 필터링할 수 있습니다. filter는 Couchbase Python SDK에서 지원하는 유효한 SearchQuery일 수 있습니다. 이러한 필터는 Vector Search가 수행되기 전에 적용됩니다. metadata의 필드 중 하나를 기준으로 필터링하려면 .을 사용하여 지정해야 합니다. 예를 들어, metadata의 source 필드를 가져오려면 metadata.source를 지정해야 합니다. filter는 Search Index에서 지원되어야 한다는 점에 유의하세요.
from couchbase import search

query = "Are there any concerning financial news?"
filter_on_source = search.MatchQuery("news", field="metadata.source")
results = vector_store.similarity_search_with_score(
    query, fields=["metadata.source"], filter=filter_on_source, k=5
)
for res, score in results:
    print(f"* {res.page_content} [{res.metadata}] {score}")
* The stock market is down 500 points today due to fears of a recession. [{'source': 'news'}] 0.3873019218444824
* Robbers broke into the city bank and stole $1 million in cash. [{'source': 'news'}] 0.20637212693691254
* The weather forecast for tomorrow is cloudy and overcast, with a high of 62 degrees. [{'source': 'news'}] 0.10404900461435318

Specifying Fields to Return

검색 시 fields 파라미터를 사용하여 문서에서 반환할 필드를 지정할 수 있습니다. 이러한 필드는 반환되는 Document의 metadata 객체의 일부로 반환됩니다. Search index에 저장된 모든 필드를 가져올 수 있습니다. 문서의 text_key는 문서의 page_content의 일부로 반환됩니다. 가져올 필드를 지정하지 않으면, 인덱스에 저장된 모든 필드가 반환됩니다. metadata의 필드 중 하나를 가져오려면 .을 사용하여 지정해야 합니다. 예를 들어, metadata의 source 필드를 가져오려면 metadata.source를 지정해야 합니다.
query = "What did I eat for breakfast today?"
results = vector_store.similarity_search(query, fields=["metadata.source"])
print(results[0])
page_content='I had chocolate chip pancakes and scrambled eggs for breakfast this morning.' metadata={'source': 'tweet'}

Query by turning into retriever

체인에서 더 쉽게 사용하기 위해 vector store를 retriever로 변환할 수도 있습니다. 다음은 vector store를 retriever로 변환한 뒤 간단한 query와 filter로 retriever를 호출하는 방법입니다.
retriever = vector_store.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 1, "score_threshold": 0.5},
)
filter_on_source = search.MatchQuery("news", field="metadata.source")
retriever.invoke("Stealing from the bank is a crime", filter=filter_on_source)
[Document(id='c7082b74-b385-4c4b-bbe5-0740909c01db', metadata={'source': 'news'}, page_content='Robbers broke into the city bank and stole $1 million in cash.')]

Hybrid Queries

Couchbase는 문서의 비-벡터 필드(예: metadata 객체)에 대한 검색과 Vector Search 결과를 결합하여 하이브리드 검색을 수행할 수 있습니다. 결과는 Vector Search와 Search Service가 지원하는 검색의 결과 조합을 기반으로 합니다. 각 구성 검색의 score를 합산해 최종 score를 계산합니다. 하이브리드 검색을 수행하려면 모든 similarity search에 전달할 수 있는 선택적 파라미터 search_options가 있습니다. search_options에 대한 다양한 검색/쿼리 가능성은 여기에서 확인할 수 있습니다. 하이브리드 검색을 시뮬레이션하기 위해 기존 문서에서 임의의 metadata를 생성해 보겠습니다. metadata에 균등하게 세 가지 필드를 추가합니다: 2010년과 2020년 사이의 date, 1에서 5 사이의 rating, 그리고 author는 John Doe 또는 Jane Doe로 설정합니다.
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter

loader = TextLoader("../../how_to/state_of_the_union.txt")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

# Adding metadata to documents
for i, doc in enumerate(docs):
    doc.metadata["date"] = f"{range(2010, 2020)[i % 10]}-01-01"
    doc.metadata["rating"] = range(1, 6)[i % 5]
    doc.metadata["author"] = ["John Doe", "Jane Doe"][i % 2]

vector_store.add_documents(docs)

query = "What did the president say about Ketanji Brown Jackson"
results = vector_store.similarity_search(query)
print(results[0].metadata)
{'author': 'John Doe', 'date': '2016-01-01', 'rating': 2, 'source': '../../how_to/state_of_the_union.txt'}

Query by Exact Value

metadata 객체의 author와 같은 텍스트 필드에서 정확히 일치하는 항목을 검색할 수 있습니다.
query = "What did the president say about Ketanji Brown Jackson"
results = vector_store.similarity_search(
    query,
    search_options={"query": {"field": "metadata.author", "match": "John Doe"}},
    fields=["metadata.author"],
)
print(results[0])
page_content='One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.

And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.' metadata={'author': 'John Doe'}

Query by Partial Match

검색에 fuzziness를 지정하여 부분 일치를 검색할 수 있습니다. 이는 검색어의 약간의 변형이나 오타를 찾고자 할 때 유용합니다. 여기서 “Jae”는 “Jane”과 가깝습니다(fuzziness 1).
query = "What did the president say about Ketanji Brown Jackson"
results = vector_store.similarity_search(
    query,
    search_options={
        "query": {"field": "metadata.author", "match": "Jae", "fuzziness": 1}
    },
    fields=["metadata.author"],
)
print(results[0])
page_content='A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.

And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system.' metadata={'author': 'Jane Doe'}

Query by Date Range Query

metadata.date와 같은 날짜 필드에 대해 날짜 범위 쿼리에 해당하는 문서를 검색할 수 있습니다.
query = "Any mention about independence?"
results = vector_store.similarity_search(
    query,
    search_options={
        "query": {
            "start": "2016-12-31",
            "end": "2017-01-02",
            "inclusive_start": True,
            "inclusive_end": False,
            "field": "metadata.date",
        }
    },
)
print(results[0])
page_content='And with 75% of adult Americans fully vaccinated and hospitalizations down by 77%, most Americans can remove their masks, return to work, stay in the classroom, and move forward safely.

We achieved this because we provided free vaccines, treatments, tests, and masks.

Of course, continuing this costs money.

I will soon send Congress a request.

The vast majority of Americans have used these tools and may want to again, so I expect Congress to pass it quickly.' metadata={'author': 'Jane Doe', 'date': '2017-01-01', 'rating': 3, 'source': '../../how_to/state_of_the_union.txt'}

Query by Numeric Range Query

metadata.rating과 같은 숫자 필드에서 범위에 해당하는 문서를 검색할 수 있습니다.
query = "Any mention about independence?"
results = vector_store.similarity_search_with_score(
    query,
    search_options={
        "query": {
            "min": 3,
            "max": 5,
            "inclusive_min": True,
            "inclusive_max": True,
            "field": "metadata.rating",
        }
    },
)
print(results[0])
(Document(id='3a90405c0f5b4c09a6646259678f1f61', metadata={'author': 'John Doe', 'date': '2014-01-01', 'rating': 5, 'source': '../../how_to/state_of_the_union.txt'}, page_content='In this Capitol, generation after generation, Americans have debated great questions amid great strife, and have done great things. \n\nWe have fought for freedom, expanded liberty, defeated totalitarianism and terror. \n\nAnd built the strongest, freest, and most prosperous nation the world has ever known. \n\nNow is the hour. \n\nOur moment of responsibility. \n\nOur test of resolve and conscience, of history itself.'), 0.3573387440020518)

Combining Multiple Search Queries

여러 검색 쿼리는 AND(conjuncts) 또는 OR(disjuncts) 연산자로 결합할 수 있습니다. 이 예제에서는 rating이 3과 4 사이이고 날짜가 2015년과 2018년 사이인 문서를 확인합니다.
query = "Any mention about independence?"
results = vector_store.similarity_search_with_score(
    query,
    search_options={
        "query": {
            "conjuncts": [
                {"min": 3, "max": 4, "inclusive_max": True, "field": "metadata.rating"},
                {"start": "2016-12-31", "end": "2017-01-02", "field": "metadata.date"},
            ]
        }
    },
)
print(results[0])
(Document(id='7115a704877a46ad94d661dd9c81cbc3', metadata={'author': 'Jane Doe', 'date': '2017-01-01', 'rating': 3, 'source': '../../how_to/state_of_the_union.txt'}, page_content='And with 75% of adult Americans fully vaccinated and hospitalizations down by 77%, most Americans can remove their masks, return to work, stay in the classroom, and move forward safely. \n\nWe achieved this because we provided free vaccines, treatments, tests, and masks. \n\nOf course, continuing this costs money. \n\nI will soon send Congress a request. \n\nThe vast majority of Americans have used these tools and may want to again, so I expect Congress to pass it quickly.'), 0.6898253780130769)
Note 하이브리드 검색 결과에는 모든 검색 파라미터를 만족하지 않는 문서가 포함될 수 있습니다. 이는 scoring이 계산되는 방식 때문입니다. score는 vector search score와 하이브리드 검색의 쿼리 score의 합입니다. Vector Search score가 높으면, 모든 하이브리드 쿼리를 일치시키는 결과보다 결합된 score가 더 높을 수 있습니다. 이러한 결과를 피하려면 하이브리드 검색 대신 filter 파라미터를 사용하세요.

Combining Hybrid Search Query with Filters

요구 사항에 맞는 결과를 얻기 위해 하이브리드 검색과 필터를 결합할 수 있습니다. 이 예제에서는 rating이 3과 5 사이이고 텍스트 필드에 “independence” 문자열이 일치하는 문서를 확인합니다.
filter_text = search.MatchQuery("independence", field="text")

query = "Any mention about independence?"
results = vector_store.similarity_search_with_score(
    query,
    search_options={
        "query": {
            "min": 3,
            "max": 5,
            "inclusive_min": True,
            "inclusive_max": True,
            "field": "metadata.rating",
        }
    },
    filter=filter_text,
)

print(results[0])
(Document(id='23bb51b4e4d54a94ab0a95e72be8428c', metadata={'author': 'John Doe', 'date': '2012-01-01', 'rating': 3, 'source': '../../how_to/state_of_the_union.txt'}, page_content='And we remain clear-eyed. The Ukrainians are fighting back with pure courage. But the next few days weeks, months, will be hard on them.  \n\nPutin has unleashed violence and chaos.  But while he may make gains on the battlefield – he will pay a continuing high price over the long run. \n\nAnd a proud Ukrainian people, who have known 30 years  of independence, have repeatedly shown that they will not tolerate anyone who tries to take their country backwards.'), 0.30549919644400614)

Other Queries

마찬가지로, search_options 파라미터에서 Geo Distance, Polygon Search, Wildcard, Regular Expressions 등 지원되는 모든 Query 메서드를 사용할 수 있습니다. 사용 가능한 쿼리 메서드 및 구문에 대한 자세한 내용은 문서를 참조하세요.

Usage for retrieval-augmented generation

이 vector store를 retrieval-augmented generation(RAG)에 사용하는 방법에 대한 가이드는 다음 섹션을 참조하세요:

Frequently Asked Questions

Question: CouchbaseSearchVectorStore 객체를 생성하기 전에 Search index를 만들어야 하나요?

네, 현재는 CouchbaseSearchVectoreStore 객체를 생성하기 전에 Search index를 생성해야 합니다.

Question: 검색 결과에서 지정한 모든 필드를 볼 수 없습니다

Couchbase에서는 Search index에 저장된 필드만 반환할 수 있습니다. 검색 결과에서 액세스하려는 필드가 Search index의 일부인지 확인하세요. 이를 처리하는 한 가지 방법은 인덱스에서 문서의 필드를 동적으로 색인화(Index)하고 저장(Store)하는 것입니다.
  • Capella에서는 “Advanced Mode”로 이동한 다음, 꺾쇠(chevron) “General Settings” 아래에서 “[X] Store Dynamic Fields” 또는 “[X] Index Dynamic Fields”를 체크할 수 있습니다
  • Couchbase Server에서는 Index Editor(Quick Editor 아님)의 꺾쇠(chevron) “Advanced” 아래에서 “[X] Store Dynamic Fields” 또는 “[X] Index Dynamic Fields”를 체크할 수 있습니다
이 옵션들은 인덱스 크기를 증가시킨다는 점에 유의하세요. 동적 매핑에 대한 자세한 내용은 documentation을 참조하세요.

Question: 검색 결과에서 metadata 객체를 볼 수 없습니다

이는 문서의 metadata 필드가 Couchbase Search index에 의해 인덱싱 및/또는 저장되지 않았기 때문일 가능성이 높습니다. 문서의 metadata 필드를 인덱싱하려면, 인덱스에 child mapping으로 추가해야 합니다. 매핑에서 모든 필드를 매핑하도록 선택하면 모든 metadata 필드로 검색할 수 있습니다. 또는 인덱스를 최적화하기 위해 metadata 객체 내부의 특정 필드만 인덱싱하도록 선택할 수 있습니다. child mapping 인덱싱에 대해 더 알아보려면 docs를 참조하세요. Creating Child Mappings

Question: filter와 search_options / 하이브리드 쿼리의 차이는 무엇인가요?

Filters는 Search index에서 검색되는 문서를 제한하는 pre-filters입니다. Couchbase Server 7.6.4 이상에서 사용할 수 있습니다. 하이브리드 쿼리는 검색 인덱스에서 반환되는 결과를 조정하는 데 사용할 수 있는 추가 검색 쿼리입니다. Filters와 하이브리드 검색 쿼리는 약간 다른 구문을 제외하면 동일한 기능을 갖습니다. Filters는 SearchQuery 객체이고, 하이브리드 검색 쿼리는 dictionaries입니다.

API reference

CouchbaseSearchVectorStore의 모든 기능과 구성에 대한 자세한 문서는 API reference에서 확인하세요.
Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I