Yellowbrick는 클라우드와 온프레미스에서 실행되는 탄력적이고 대규모 병렬 처리(MPP) SQL 데이터베이스로, 확장성, 복원력 및 클라우드 이식성을 위해 kubernetes를 사용합니다. Yellowbrick는 가장 크고 복잡한 비즈니스 크리티컬 데이터 웨어하우징 사용 사례를 해결하도록 설계되었습니다. Yellowbrick가 제공하는 대규모 효율성은 SQL로 벡터를 저장하고 검색하는 고성능 및 확장 가능한 vector database로도 사용할 수 있게 합니다.

ChatGpt의 vector store로 Yellowbrick 사용하기

이 튜토리얼은 Retrieval Augmented Generation (RAG)을 지원하기 위해 Yellowbrick를 vector store로 사용하는 ChatGpt 기반의 간단한 챗봇을 만드는 방법을 보여줍니다. 필요한 것:
  1. Yellowbrick sandbox 계정
  2. OpenAI의 api key
튜토리얼은 다섯 부분으로 나뉩니다. 먼저 langchain을 사용하여 vector store 없이 ChatGpt와 상호작용하는 기본 챗봇을 만듭니다. 두 번째로, vector store를 나타낼 embeddings 테이블을 Yellowbrick에 생성합니다. 세 번째로, 일련의 문서(Yellowbrick 매뉴얼의 Administration 챕터)를 로드합니다. 네 번째로, 해당 문서의 vector 표현을 생성하고 Yellowbrick 테이블에 저장합니다. 마지막으로, 개선된 챗박스에 동일한 쿼리를 보내 결과를 확인합니다.
# Install all needed libraries
pip install -qU  langchain
pip install -qU  langchain-openai langchain-community
pip install -qU  psycopg2-binary
pip install -qU  tiktoken

Setup: Yellowbrick 및 OpenAI API 연결에 사용되는 정보 입력

우리의 챗봇은 langchain 라이브러리를 통해 ChatGpt와 통합되므로, 먼저 OpenAI의 API key가 필요합니다: OpenAI의 api key를 얻으려면:
  1. platform.openai.com/에서 등록
  2. 결제 수단 추가 - 무료 할당량을 초과할 가능성은 낮습니다
  3. API key 생성
또한 Yellowbrick Sandbox 계정에 가입할 때 받은 환영 이메일에서 Username, Password 및 Database 이름이 필요합니다. 다음은 Yellowbrick 데이터베이스 및 OpenAPI Key 정보를 포함하도록 수정되어야 합니다
# Modify these values to match your Yellowbrick Sandbox and OpenAI API Key
YBUSER = "[SANDBOX USER]"
YBPASSWORD = "[SANDBOX PASSWORD]"
YBDATABASE = "[SANDBOX_DATABASE]"
YBHOST = "trialsandbox.sandbox.aws.yellowbrickcloud.com"

OPENAI_API_KEY = "[OPENAI API KEY]"
# Import libraries and setup keys / login info
import os
import pathlib
import re
import sys
import urllib.parse as urlparse
from getpass import getpass

import psycopg2
from IPython.display import Markdown, display
from langchain.chains import LLMChain, RetrievalQAWithSourcesChain
from langchain_community.vectorstores import Yellowbrick
from langchain_core.documents import Document
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter

# Establish connection parameters to Yellowbrick.  If you've signed up for Sandbox, fill in the information from your welcome mail here:
yellowbrick_connection_string = (
    f"postgres://{urlparse.quote(YBUSER)}:{YBPASSWORD}@{YBHOST}:5432/{YBDATABASE}"
)

YB_DOC_DATABASE = "sample_data"
YB_DOC_TABLE = "yellowbrick_documentation"
embedding_table = "my_embeddings"

# API Key for OpenAI.  Signup at https://platform.openai.com
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY

from langchain_core.prompts.chat import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    SystemMessagePromptTemplate,
)

Part 1: Vector Store 없이 ChatGpt 기반의 기본 챗봇 만들기

langchain을 사용하여 ChatGPT를 쿼리합니다. Vector Store가 없으므로 ChatGPT는 질문에 답할 컨텍스트가 없습니다.
# Set up the chat model and specific prompt
system_template = """If you don't know the answer, Make up your best guess."""
messages = [
    SystemMessagePromptTemplate.from_template(system_template),
    HumanMessagePromptTemplate.from_template("{question}"),
]
prompt = ChatPromptTemplate.from_messages(messages)

chain_type_kwargs = {"prompt": prompt}
llm = ChatOpenAI(
    model_name="gpt-3.5-turbo",  # Modify model_name if you have access to GPT-4
    temperature=0,
    max_tokens=256,
)

chain = LLMChain(
    llm=llm,
    prompt=prompt,
    verbose=False,
)


def print_result_simple(query):
    result = chain(query)
    output_text = f"""### Question:
  {query}
  ### Answer:
  {result["text"]}
    """
    display(Markdown(output_text))


# Use the chain to query
print_result_simple("How many databases can be in a Yellowbrick Instance?")

print_result_simple("What's an easy way to add users in bulk to Yellowbrick?")

Part 2: Yellowbrick에 연결하고 embedding 테이블 생성

문서 embeddings를 Yellowbrick에 로드하려면 저장할 자체 테이블을 생성해야 합니다. 테이블이 있는 Yellowbrick 데이터베이스는 UTF-8로 인코딩되어야 합니다. 다음 스키마로 UTF-8 데이터베이스에 테이블을 생성하고, 원하는 테이블 이름을 제공하세요:
# Establish a connection to the Yellowbrick database
try:
    conn = psycopg2.connect(yellowbrick_connection_string)
except psycopg2.Error as e:
    print(f"Error connecting to the database: {e}")
    exit(1)

# Create a cursor object using the connection
cursor = conn.cursor()

# Define the SQL statement to create a table
create_table_query = f"""
CREATE TABLE IF NOT EXISTS {embedding_table} (
    doc_id uuid NOT NULL,
    embedding_id smallint NOT NULL,
    embedding double precision NOT NULL
)
DISTRIBUTE ON (doc_id);
truncate table {embedding_table};
"""

# Execute the SQL query to create a table
try:
    cursor.execute(create_table_query)
    print(f"Table '{embedding_table}' created successfully!")
except psycopg2.Error as e:
    print(f"Error creating table: {e}")
    conn.rollback()

# Commit changes and close the cursor and connection
conn.commit()
cursor.close()
conn.close()

Part 3: Yellowbrick의 기존 테이블에서 인덱싱할 문서 추출

기존 Yellowbrick 테이블에서 문서 경로와 내용을 추출합니다. 다음 단계에서 이 문서들을 사용하여 embeddings를 생성합니다.
yellowbrick_doc_connection_string = (
    f"postgres://{urlparse.quote(YBUSER)}:{YBPASSWORD}@{YBHOST}:5432/{YB_DOC_DATABASE}"
)

print(yellowbrick_doc_connection_string)

# Establish a connection to the Yellowbrick database
conn = psycopg2.connect(yellowbrick_doc_connection_string)

# Create a cursor object
cursor = conn.cursor()

# Query to select all documents from the table
query = f"SELECT path, document FROM {YB_DOC_TABLE}"

# Execute the query
cursor.execute(query)

# Fetch all documents
yellowbrick_documents = cursor.fetchall()

print(f"Extracted {len(yellowbrick_documents)} documents successfully!")

# Close the cursor and connection
cursor.close()
conn.close()

Part 4: 문서로 Yellowbrick Vector Store 로드

문서를 처리하고, 소화 가능한 청크로 분할하고, embedding을 생성하여 Yellowbrick 테이블에 삽입합니다. 약 5분 정도 걸립니다.
# Split documents into chunks for conversion to embeddings
DOCUMENT_BASE_URL = "https://docs.yellowbrick.com/6.7.1/"  # Actual URL


separator = "\n## "  # This separator assumes Markdown docs from the repo uses ### as logical main header most of the time
chunk_size_limit = 2000
max_chunk_overlap = 200

documents = [
    Document(
        page_content=document[1],
        metadata={"source": DOCUMENT_BASE_URL + document[0].replace(".md", ".html")},
    )
    for document in yellowbrick_documents
]

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=chunk_size_limit,
    chunk_overlap=max_chunk_overlap,
    separators=[separator, "\nn", "\n", ",", " ", ""],
)
split_docs = text_splitter.split_documents(documents)

docs_text = [doc.page_content for doc in split_docs]

embeddings = OpenAIEmbeddings()
vector_store = Yellowbrick.from_documents(
    documents=split_docs,
    embedding=embeddings,
    connection_string=yellowbrick_connection_string,
    table=embedding_table,
)

print(f"Created vector store with {len(documents)} documents")

Part 5: Yellowbrick를 vector store로 사용하는 챗봇 만들기

다음으로, Yellowbrick를 vector store로 추가합니다. vector store는 Yellowbrick 제품 문서의 관리 챕터를 나타내는 embeddings로 채워졌습니다. 개선된 응답을 확인하기 위해 위와 동일한 쿼리를 보냅니다.
system_template = """Use the following pieces of context to answer the users question.
Take note of the sources and include them in the answer in the format: "SOURCES: source1 source2", use "SOURCES" in capital letters regardless of the number of sources.
If you don't know the answer, just say that "I don't know", don't try to make up an answer.
----------------
{summaries}"""
messages = [
    SystemMessagePromptTemplate.from_template(system_template),
    HumanMessagePromptTemplate.from_template("{question}"),
]
prompt = ChatPromptTemplate.from_messages(messages)

vector_store = Yellowbrick(
    OpenAIEmbeddings(),
    yellowbrick_connection_string,
    embedding_table,  # Change the table name to reflect your embeddings
)

chain_type_kwargs = {"prompt": prompt}
llm = ChatOpenAI(
    model_name="gpt-3.5-turbo",  # Modify model_name if you have access to GPT-4
    temperature=0,
    max_tokens=256,
)
chain = RetrievalQAWithSourcesChain.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vector_store.as_retriever(search_kwargs={"k": 5}),
    return_source_documents=True,
    chain_type_kwargs=chain_type_kwargs,
)


def print_result_sources(query):
    result = chain(query)
    output_text = f"""### Question:
  {query}
  ### Answer:
  {result["answer"]}
  ### Sources:
  {result["sources"]}
  ### All relevant sources:
  {", ".join(list(set([doc.metadata["source"] for doc in result["source_documents"]])))}
    """
    display(Markdown(output_text))


# Use the chain to query

print_result_sources("How many databases can be in a Yellowbrick Instance?")

print_result_sources("Whats an easy way to add users in bulk to Yellowbrick?")

Part 6: 성능 향상을 위한 Index 도입

Yellowbrick는 Locality-Sensitive Hashing 접근 방식을 사용한 인덱싱도 지원합니다. 이는 근사 최근접 이웃 검색 기법으로, 정확도를 희생하여 유사성 검색 시간을 절충할 수 있습니다. index는 두 가지 새로운 조정 가능한 매개변수를 도입합니다:
  • hyperplane의 수는 create_lsh_index(num_hyperplanes)에 인수로 제공됩니다. 문서가 많을수록 더 많은 hyperplane이 필요합니다. LSH는 차원 축소의 한 형태입니다. 원본 embeddings는 구성 요소의 수가 hyperplane의 수와 동일한 저차원 벡터로 변환됩니다.
  • Hamming distance는 검색 범위를 나타내는 정수입니다. Hamming distance가 작을수록 검색 속도는 빠르지만 정확도는 낮아집니다.
Yellowbrick에 로드한 embeddings에 index를 생성하는 방법은 다음과 같습니다. 이전 채팅 세션을 다시 실행하지만, 이번에는 검색에 index를 사용합니다. 이렇게 적은 수의 문서에서는 성능 측면에서 인덱싱의 이점을 볼 수 없습니다.
system_template = """Use the following pieces of context to answer the users question.
Take note of the sources and include them in the answer in the format: "SOURCES: source1 source2", use "SOURCES" in capital letters regardless of the number of sources.
If you don't know the answer, just say that "I don't know", don't try to make up an answer.
----------------
{summaries}"""
messages = [
    SystemMessagePromptTemplate.from_template(system_template),
    HumanMessagePromptTemplate.from_template("{question}"),
]
prompt = ChatPromptTemplate.from_messages(messages)

vector_store = Yellowbrick(
    OpenAIEmbeddings(),
    yellowbrick_connection_string,
    embedding_table,  # Change the table name to reflect your embeddings
)

lsh_params = Yellowbrick.IndexParams(
    Yellowbrick.IndexType.LSH, {"num_hyperplanes": 8, "hamming_distance": 2}
)
vector_store.create_index(lsh_params)

chain_type_kwargs = {"prompt": prompt}
llm = ChatOpenAI(
    model_name="gpt-3.5-turbo",  # Modify model_name if you have access to GPT-4
    temperature=0,
    max_tokens=256,
)
chain = RetrievalQAWithSourcesChain.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vector_store.as_retriever(
        k=5, search_kwargs={"index_params": lsh_params}
    ),
    return_source_documents=True,
    chain_type_kwargs=chain_type_kwargs,
)


def print_result_sources(query):
    result = chain(query)
    output_text = f"""### Question:
  {query}
  ### Answer:
  {result["answer"]}
  ### Sources:
  {result["sources"]}
  ### All relevant sources:
  {", ".join(list(set([doc.metadata["source"] for doc in result["source_documents"]])))}
    """
    display(Markdown(output_text))


# Use the chain to query

print_result_sources("How many databases can be in a Yellowbrick Instance?")

print_result_sources("Whats an easy way to add users in bulk to Yellowbrick?")

Next Steps

이 코드는 다른 질문을 하도록 수정할 수 있습니다. vector store에 자신의 문서를 로드할 수도 있습니다. langchain 모듈은 매우 유연하며 다양한 파일(HTML, PDF 등 포함)을 파싱할 수 있습니다. 완전히 프라이빗한 챗박스 경험을 위해 Huggingface embeddings 모델과 Meta의 Llama 2 LLM을 사용하도록 수정할 수도 있습니다.
Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I