Alibaba Cloud MySQL

Alibaba Cloud MySQL

ApsaraDB RDS for MySQL(Alibaba Cloud MySQL)을 벡터 스토어로 사용해 RAG를 구축하는 데모예요. DashScope 임베딩·LLM을 함께 써서 기본 검색과 메타데이터 필터링까지 실습해 볼게요.

출처: 문서

본문

Alibaba Cloud MySQL은 ApsaraDB RDS for MySQL이라고도 합니다. ApsaraDB RDS for MySQL은 MySQL 소스 코드의 한 분기를 기반으로 하는 온라인 데이터베이스 서비스로, 고성능을 제공합니다. 더블 11(Double 11) 기간 대규모 동시 트래픽을 처리하며 검증된 솔루션입니다. 화이트리스트 구성, 백업 및 복구, TDE(Transparent Data Encryption), 데이터 마이그레이션, 인스턴스·계정·데이터베이스 관리를 포함한 기본 기능을 제공합니다. 자세한 내용은 RDS MySQL Feature Overview를 참고하세요.

이 노트북을 실행하려면 클라우드에서 실행 중인 ApsaraDB RDS MySQL 인스턴스가 필요하며, 계정을 만들고 필요한 데이터베이스를 생성해야 합니다. 이 링크를 참고하세요.

이 노트북에서는 ApsaraDB RDS MySQL 인스턴스에 llama_index_test와 llama_index_meta_test라는 데이터베이스를 만들어야 합니다.

Setup

이 노트북을 colab에서 여는 경우 llama-index가 설치돼 있는지 확인해야 할 수 있습니다:

!pip install llama-index
%pip install llama-index-vector-stores-alibabacloud-mysql
# choose dashscope as embedding and llm model, your can also use default openai or other model to test
%pip install llama-index-embeddings-dashscope
%pip install llama-index-llms-dashscope

dashscope 임베딩과 LLM 모델을 구성합니다. 기본 openai나 다른 모델로 테스트해도 됩니다. dashscope 모델을 사용한다면 API 키를 여기에서 받아 다음 코드에 설정합니다:

!export DASHSCOPE_API_KEY="your_api_key"

예제 데이터 다운로드

!mkdir -p 'data/paul_graham/'
!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham/paul_graham_essay.txt'

Alibaba Cloud MySQL을 사용한 RAG 데모

단순 검색 (Simple Query)

단순 검색용 데이터 로드

import os


DASHSCOPE_API_KEY = os.environ.get("DASHSCOPE_API_KEY")


from llama_index.core import (
    VectorStoreIndex,
    SimpleDirectoryReader,
    StorageContext,
)
from llama_index.vector_stores.alibabacloud_mysql.base import (
    AlibabaCloudMySQLVectorStore,
)


# set Embbeding model
from llama_index.core import Settings
from llama_index.embeddings.dashscope import DashScopeEmbedding


Settings.embed_model = DashScopeEmbedding(api_key=DASHSCOPE_API_KEY)


# config llm model
from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels


dashscope_llm = DashScope(
    model_name=DashScopeGenerationModels.QWEN_MAX, api_key=DASHSCOPE_API_KEY
)


documents = SimpleDirectoryReader("data/paul_graham/").load_data()
print(f"Total documents: {len(documents)}")
print(f"First document, id: {documents[0].doc_id}")
print(f"First document, hash: {documents[0].hash}")
print(
    "First document, text"
    f" ({len(documents[0].text)} characters):\n{'='*20}\n{documents[0].text[:360]} ..."
)


print(
    """
#################
# simple generate vector
#################"""
)
client = AlibabaCloudMySQLVectorStore.from_params(
    host="rm-***.mysql.***.rds.aliyuncs.com",
    port=3306,
    user="user",
    password="password",
    database="llama_index_test",
    distance_method="COSINE",
)
storage_context = StorageContext.from_defaults(vector_store=client)
VectorStoreIndex.from_documents(
    documents, storage_context=storage_context, show_progress=True
)

AlibabaCloudMySQL로 검색 테스트 포함 쿼리

import os


DASHSCOPE_API_KEY = os.environ.get("DASHSCOPE_API_KEY")


from llama_index.core import VectorStoreIndex
from llama_index.vector_stores.alibabacloud_mysql.base import (
    AlibabaCloudMySQLVectorStore,
)


# set Embbeding model
from llama_index.core import Settings
from llama_index.embeddings.dashscope import DashScopeEmbedding


embed_model = DashScopeEmbedding(api_key=DASHSCOPE_API_KEY)
# Global Settings
Settings.embed_model = embed_model


# config llm model
from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels


dashscope_llm = DashScope(
    model_name=DashScopeGenerationModels.QWEN_MAX, api_key=DASHSCOPE_API_KEY
)


print(
    """
#################
# Basic Querying including Search Test
#################
"""
)
client = AlibabaCloudMySQLVectorStore.from_params(
    host="rm-***.mysql.eu-west-1.rds.***.com",
    port=3306,
    user="user",
    password="password",
    database="llama_index_test",
    distance_method="COSINE",
)
index = VectorStoreIndex.from_vector_store(
    vector_store=client, embed_model=embed_model
)


QUESTION = "What did the author do growing up?"
# Set Retriever
vector_retriever = index.as_retriever()
# search
source_nodes = vector_retriever.retrieve(QUESTION)
# check source_nodes
print(f"Question: {QUESTION}")
for node in source_nodes:
    print(f"---------------------------------------------")
    print("Search Test")
    print(f"---------------------------------------------")
    print(f"Score: {node.score:.3f}")
    print(node.get_content())
    print(f"---------------------------------------------")


# run query
query_engine = index.as_query_engine(llm=dashscope_llm)
res = query_engine.query(QUESTION)
print(f"Answer: {res.response}")
print(f"---------------------------------------------\n\n")

메타데이터 필터링 (Metadata Filtering)

메타데이터 필터링용 데이터 로드

import os


DASHSCOPE_API_KEY = os.environ.get("DASHSCOPE_API_KEY")


from llama_index.core import (
    VectorStoreIndex,
    SimpleDirectoryReader,
    StorageContext,
)
from llama_index.vector_stores.alibabacloud_mysql.base import (
    AlibabaCloudMySQLVectorStore,
)


# set Embbeding model
from llama_index.core import Settings
from llama_index.embeddings.dashscope import DashScopeEmbedding


Settings.embed_model = DashScopeEmbedding(api_key=DASHSCOPE_API_KEY)


# config llm model
from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels


dashscope_llm = DashScope(
    model_name=DashScopeGenerationModels.QWEN_MAX, api_key=DASHSCOPE_API_KEY
)


documents = SimpleDirectoryReader("data/paul_graham/").load_data()
print(f"Total documents: {len(documents)}")
print(f"First document, id: {documents[0].doc_id}")
print(f"First document, hash: {documents[0].hash}")
print(
    "First document, text"
    f" ({len(documents[0].text)} characters):\n{'='*20}\n{documents[0].text[:360]} ..."
)


print(
    """
#################
# generate vector with some metadata for Metadata Filtering
#################"""
)
client = AlibabaCloudMySQLVectorStore.from_params(
    host="rm-***.mysql.***.rds.aliyuncs.com",
    port=3306,
    user="user",
    password="password",
    database="llama_index_meta_test",
    distance_method="COSINE",
)
storage_context = StorageContext.from_defaults(vector_store=client)
index = VectorStoreIndex.from_documents(
    documents, storage_context=storage_context, show_progress=True
)


from llama_index.core import Document
import regex as re


# Split the text into paragraphs.
text_chunks = documents[0].text.split("\n\n")


# Create a document for each footnote
footnotes = [
    Document(
        text=chunk,
        id=documents[0].doc_id,
        metadata={
            "is_footnote": bool(re.search(r"^\s*\[\d+\]\s*", chunk)),
            "mark_id": i,
        },
    )
    for i, chunk in enumerate(text_chunks)
    if bool(re.search(r"^\s*\[\d+\]\s*", chunk))
]


# Insert the footnotes into the index
for f in footnotes:
    index.insert(f)

텍스트를 문단 단위로 나눈 뒤 각주(footnote) 패턴([숫자]로 시작)에 해당하는 청크를 개별 Document로 만들어 is_footnote(불리언)와 mark_id(순서) 메타데이터를 붙여 인덱스에 삽입합니다.

메타데이터 필터링 포함 쿼리

import os


DASHSCOPE_API_KEY = os.environ.get("DASHSCOPE_API_KEY")


from llama_index.core import VectorStoreIndex
from llama_index.vector_stores.alibabacloud_mysql.base import (
    AlibabaCloudMySQLVectorStore,
)


# set Embbeding model
from llama_index.core import Settings
from llama_index.embeddings.dashscope import DashScopeEmbedding


embed_model = DashScopeEmbedding(api_key=DASHSCOPE_API_KEY)
# Global Settings
Settings.embed_model = embed_model


# config llm model
from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels


dashscope_llm = DashScope(
    model_name=DashScopeGenerationModels.QWEN_MAX, api_key=DASHSCOPE_API_KEY
)


print(
    """
#################
# Querying with Metadata Filtering including Search Test
#################
"""
)
client = AlibabaCloudMySQLVectorStore.from_params(
    host="rm-***.mysql.***.rds.aliyuncs.com",
    port=3306,
    user="user",
    password="password",
    database="llama_index_meta_test",
    distance_method="COSINE",
)
index = VectorStoreIndex.from_vector_store(
    vector_store=client, embed_model=embed_model
)


from llama_index.core.vector_stores import (
    MetadataFilters,
    MetadataFilter,
    FilterOperator,
    FilterCondition,
)


QUESTION = "What did the author about space aliens and lisp?"
print(f"---------------------------------------------")
print(f"Question: {QUESTION}")
filters = MetadataFilters(
    filters=[
        MetadataFilter(
            key="is_footnote", value="true", operator=FilterOperator.EQ
        ),
        MetadataFilter(key="mark_id", value=0, operator=FilterOperator.GTE),
    ],
    condition=FilterCondition.AND,
)
print(f"---------------------------------------------")
for i in range(len(filters.filters)):
    print(f"Filter[{i}]: {filters.filters[i]}")
print(f"Filter Condition: {filters.condition}")
print(f"---------------------------------------------")
retriever = index.as_retriever(
    filters=filters,
)
result = retriever.retrieve(QUESTION)
for node in result:
    print("Search Test")
    print(f"---------------------------------------------")
    print(f"Score: {node.score:.3f}")
    print(node.get_content())
    print(f"---------------------------------------------")


# Create a query engine that only searches certain footnotes.
footnote_query_engine = index.as_query_engine(
    filters=filters,
    llm=dashscope_llm,
)


res = footnote_query_engine.query(QUESTION)
print(f"Answer: {res.response}")
print(f"---------------------------------------------\n\n")

MetadataFilters에 is_footnote == true와 mark_id >= 0 두 조건을 AND로 결합해, 쿼리 시 특정 메타데이터를 가진 노드만 검색 대상으로 제한합니다. 이를 활용하면 원문 전체 대신 특정 각주들만 검색하는 쿼리 엔진을 만들 수 있습니다.

더 알아보기 (Learn more)