Milvus 는 심층 신경망과 기타 머신 러닝(ML) 모델이 생성한 대규모 임베딩 벡터를 저장, 인덱싱, 관리하는 데이터베이스입니다.
이 노트북은 Milvus vector database와 관련된 기능을 사용하는 방법을 보여줍니다.

Setup

이 통합을 사용하려면 pip install -qU langchain-milvuslangchain-milvus를 설치해야 합니다.
pip install -qU langchain-milvus
Note: you may need to restart the kernel to use updated packages.

Credentials

Milvus vector store를 사용하는 데는 별도의 자격 증명이 필요하지 않습니다.

Initialization

# | output: false
# | echo: false
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(model="text-embedding-3-large")

Milvus Lite

프로토타이핑을 가장 쉽게 시작하는 방법은 Milvus Lite를 사용하는 것입니다. 모든 것이 로컬 vector database 파일에 저장됩니다. 사용할 수 있는 인덱스는 Flat 인덱스뿐입니다.
from langchain_milvus import Milvus

URI = "./milvus_example.db"

vector_store = Milvus(
    embedding_function=embeddings,
    connection_args={"uri": URI},
    index_params={"index_type": "FLAT", "metric_type": "L2"},
)

Milvus Server

대량의 데이터(예: 백만 개 이상의 벡터)가 있는 경우 Docker 또는 Kubernetes에서 더 높은 성능의 Milvus 서버를 설정하는 것을 권장합니다. Milvus 서버는 다양한 indexes를 지원합니다. 이러한 서로 다른 인덱스를 활용하면 요구사항에 맞춰 검색 기능을 크게 향상시키고 검색 속도를 가속화할 수 있습니다. 예시로, Milvus Standalone의 경우를 고려해 보겠습니다. Docker 컨테이너를 시작하려면 다음 명령을 실행하면 됩니다:
!curl -sfL https://raw.githubusercontent.com/milvus-io/milvus/master/scripts/standalone_embed.sh -o standalone_embed.sh

!bash standalone_embed.sh start
Password:
여기서는 Milvus 데이터베이스를 생성합니다:
from pymilvus import Collection, MilvusException, connections, db, utility

conn = connections.connect(host="127.0.0.1", port=19530)

# Check if the database exists
db_name = "milvus_demo"
try:
    existing_databases = db.list_database()
    if db_name in existing_databases:
        print(f"Database '{db_name}' already exists.")

        # Use the database context
        db.using_database(db_name)

        # Drop all collections in the database
        collections = utility.list_collections()
        for collection_name in collections:
            collection = Collection(name=collection_name)
            collection.drop()
            print(f"Collection '{collection_name}' has been dropped.")

        db.drop_database(db_name)
        print(f"Database '{db_name}' has been deleted.")
    else:
        print(f"Database '{db_name}' does not exist.")
        database = db.create_database(db_name)
        print(f"Database '{db_name}' created successfully.")
except MilvusException as e:
    print(f"An error occurred: {e}")
Database 'milvus_demo' does not exist.
Database 'milvus_demo' created successfully.
아래 URI가 변경된 것에 주의하세요. 인스턴스가 초기화되면 127.0.0.1:9091/webui로 이동하여 로컬 웹 UI를 확인할 수 있습니다. 다음은 Milvus database service로 vector store 인스턴스를 생성하는 예시입니다:
from langchain_milvus import BM25BuiltInFunction, Milvus

URI = "http://localhost:19530"

vectorstore = Milvus(
    embedding_function=embeddings,
    connection_args={"uri": URI, "token": "root:Milvus", "db_name": "milvus_demo"},
    index_params={"index_type": "FLAT", "metric_type": "L2"},
    consistency_level="Strong",
    drop_old=False,  # set to True if seeking to drop the collection with that name if it exists
)
Zilliz Cloud(완전 관리형 Milvus 클라우드 서비스)를 사용하려면, Zilliz Cloud의 Public EndpointApi key에 해당하는 uri와 token을 조정해주세요.

Compartmentalize the data with Milvus Collections

동일한 Milvus 인스턴스 내에서 관련 없는 문서를 서로 다른 collections에 저장할 수 있습니다. 새로운 collection을 생성하는 방법은 다음과 같습니다:
from langchain_core.documents import Document

vector_store_saved = Milvus.from_documents(
    [Document(page_content="foo!")],
    embeddings,
    collection_name="langchain_example",
    connection_args={"uri": URI},
)
저장된 collection을 조회하는 방법은 다음과 같습니다:
vector_store_loaded = Milvus(
    embeddings,
    connection_args={"uri": URI},
    collection_name="langchain_example",
)

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)

Delete items from vector store

vector_store.delete(ids=[uuids[-1]])
(insert count: 0, delete count: 1, upsert count: 0, timestamp: 0, success count: 0, err count: 0, cost: 0)

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,
    expr='source == "tweet"',
)
for res in results:
    print(f"* {res.page_content} [{res.metadata}]")
* Building an exciting new project with LangChain - come check it out! [{'pk': '9905001c-a4a3-455e-ab94-72d0ed11b476', 'source': 'tweet'}]
* LangGraph is the best framework for building stateful, agentic applications! [{'pk': '1206d237-ee3a-484f-baf2-b5ac38eeb314', 'source': 'tweet'}]

Similarity search with score

점수를 포함하여 검색할 수도 있습니다:
results = vector_store.similarity_search_with_score(
    "Will it be hot tomorrow?", k=1, expr='source == "news"'
)
for res, score in results:
    print(f"* [SIM={score:3f}] {res.page_content} [{res.metadata}]")
* [SIM=21192.628906] bar [{'pk': '2', 'source': 'https://example.com'}]
Milvus vector store를 사용할 때 이용 가능한 모든 검색 옵션의 전체 목록은 API reference에서 확인할 수 있습니다.

Query by turning into retriever

체인에서 더 쉽게 사용하기 위해 vector store를 retriever로 변환할 수도 있습니다.
retriever = vector_store.as_retriever(search_type="mmr", search_kwargs={"k": 1})
retriever.invoke("Stealing from the bank is a crime", filter={"source": "news"})
[Document(metadata={'pk': 'eacc7256-d7fa-4036-b1f7-83d7a4bee0c5', 'source': 'news'}, page_content='Robbers broke into the city bank and stole $1 million in cash.')]
가장 일반적인 하이브리드 검색 시나리오는 dense + sparse 하이브리드 검색입니다. 여기서는 의미론적 벡터 유사도와 정확한 키워드 매칭을 모두 사용하여 후보를 검색합니다. 이러한 방법의 결과를 병합하고 재정렬한 후 LLM에 전달하여 최종 답변을 생성합니다. 이 접근법은 정밀도와 의미 이해를 균형 있게 고려하여 다양한 쿼리 시나리오에서 매우 효과적입니다. Milvus 2.5부터는 BM25 알고리즘을 sparse vectors로 표현하는 Sparse-BM25 방식을 통해 full-text search를 기본적으로 지원합니다. Milvus는 원시 텍스트를 입력으로 받아 지정된 필드에 sparse vectors로 자동 변환하여 저장하므로, 사용자가 sparse embedding을 수동으로 생성할 필요가 없습니다. Full-text search의 경우 Milvus VectorStore는 builtin_function 파라미터를 받습니다. 이 파라미터를 통해 BM25BuiltInFunction 인스턴스를 전달할 수 있습니다. 이는 일반적으로 dense embeddings를 VectorStore에 전달하는 semantic search와는 다른 방식입니다. 다음은 semantic search를 위한 OpenAI dense embedding과 full-text search를 위한 BM25를 사용하는 Milvus의 하이브리드 검색 간단 예시입니다:
from langchain_milvus import BM25BuiltInFunction, Milvus
from langchain_openai import OpenAIEmbeddings

vectorstore = Milvus.from_documents(
    documents=documents,
    embedding=OpenAIEmbeddings(),
    builtin_function=BM25BuiltInFunction(),
    # `dense` is for OpenAI embeddings, `sparse` is the output field of BM25 function
    vector_field=["dense", "sparse"],
    connection_args={
        "uri": URI,
    },
    consistency_level="Strong",
    drop_old=True,
)
  • BM25BuiltInFunction을 사용할 때, full-text search는 Milvus Standalone 및 Milvus Distributed에서는 지원되지만 Milvus Lite에서는 아직 지원되지 않습니다(향후 포함 예정). 또한 Zilliz Cloud(완전 관리형 Milvus)에서도 곧 제공될 예정입니다. 자세한 정보는 [email protected]으로 문의하세요.
위 코드에서 BM25BuiltInFunction 인스턴스를 정의하고 이를 Milvus 객체에 전달합니다. BM25BuiltInFunction은 Milvus의 Function에 대한 경량 래퍼 클래스입니다. 이를 OpenAIEmbeddings와 함께 사용하여 dense + sparse 하이브리드 검색 Milvus vector store 인스턴스를 초기화할 수 있습니다. BM25BuiltInFunction은 클라이언트가 코퍼스나 학습 과정을 전달할 필요가 없으며, 모든 처리는 Milvus 서버 측에서 자동으로 처리되므로 사용자는 어떤 vocabulary나 corpus에 대해서도 신경 쓸 필요가 없습니다. 추가로, 사용자는 analyzer를 커스터마이징하여 BM25에서 사용자 정의 텍스트 처리를 구현할 수도 있습니다.

Rerank the candidates

1차 검색 단계 이후에는 더 나은 결과를 얻기 위해 후보를 재정렬해야 합니다. 자세한 내용은 Reranking을 참고하세요. 다음은 가중치를 활용한 reranking 예시입니다:
query = "What are the novels Lila has written and what are their contents?"

vectorstore.similarity_search(
    query, k=1, ranker_type="weighted", ranker_params={"weights": [0.6, 0.4]}
)

Usage for retrieval-augmented generation

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

Per-User Retrieval

retrieval 앱을 구축할 때는 다중 사용자를 고려해야 하는 경우가 많습니다. 이는 한 명이 아닌 여러 사용자의 데이터를 저장할 수 있으며, 서로의 데이터를 볼 수 없어야 한다는 의미입니다. Milvus는 멀티 테넌시 구현을 위해 partition_key 사용을 권장합니다. 예시는 다음과 같습니다:
Partition key 기능은 Milvus Lite에서는 사용할 수 없습니다. 이를 사용하려면 위에서 언급한 대로 Milvus 서버를 시작해야 합니다.
from langchain_core.documents import Document

docs = [
    Document(page_content="i worked at kensho", metadata={"namespace": "harrison"}),
    Document(page_content="i worked at facebook", metadata={"namespace": "ankush"}),
]
vectorstore = Milvus.from_documents(
    docs,
    embeddings,
    connection_args={"uri": URI},
    drop_old=True,
    partition_key_field="namespace",  # Use the "namespace" field as the partition key
)
partition key를 사용하여 검색하려면, 검색 요청의 boolean expression에 다음 중 하나를 포함해야 합니다: search_kwargs={"expr": '<partition_key> == "xxxx"'} search_kwargs={"expr": '<partition_key> == in ["xxx", "xxx"]'} <partition_key>를 partition key로 지정된 필드 이름으로 바꿔주세요. Milvus는 지정된 partition key를 기준으로 파티션을 선택하고, partition key에 따라 엔티티를 필터링한 다음, 필터링된 엔티티를 대상으로 검색합니다.
# This will only get documents for Ankush
vectorstore.as_retriever(search_kwargs={"expr": 'namespace == "ankush"'}).invoke(
    "where did i work?"
)
[Document(page_content='i worked at facebook', metadata={'namespace': 'ankush'})]
# This will only get documents for Harrison
vectorstore.as_retriever(search_kwargs={"expr": 'namespace == "harrison"'}).invoke(
    "where did i work?"
)
[Document(page_content='i worked at kensho', metadata={'namespace': 'harrison'})]

API reference

__ModuleName__VectorStore의 모든 기능과 설정에 대한 자세한 문서는 API reference에서 확인하세요: python.langchain.com/api_reference/milvus/vectorstores/langchain_milvus.vectorstores.milvus.Milvus.html
Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I