Solar LLM과 몽고DB Atlas를 사용하여 엔드투엔드 RAG 시스템 구축하기

2024/07/01 | 작성자: 손주형
 

들어가는 말

전 세계 주요 기업을 위한 대규모 AI 솔루션을 구축하는 엔터프라이즈 AI 스타트업인 Upstage AI는 문서 AI(주요 정보 추출기, 레이아웃 분석, OCR을 통해 비정형 문서를 기계 판독 가능한 데이터로 변환), 임베딩 모델, Solar LLM, 접지 검사기 등 풀스택 LLM 구성 요소 제품군을 제공합니다. 이러한 구성 요소와 다음과 같은 강력한 벡터 DB를 사용하면 몽고DB 아틀라스와 같은 강력한 벡터 DB를 통해 개발자는 검색 증강 생성(RAG) 애플리케이션을 구축할 수 있습니다. 

RAG 애플리케이션은 특히 Upstage와 같은 기업 사용자에게 유용합니다. 개인 데이터베이스에서 검색된 데이터를 기반으로 LLM에서 답변을 생성하므로 환각의 위험을 낮추고 생산성을 높일 수 있습니다.

이 블로그에서는 기업이 내부 RAG 애플리케이션을 구축하는 방법을 설명합니다. 직원들의 정보 접근성을 개선하고 업무 생산성을 높이기 위해 Solar LLM과 MongoDB Atlas를 사용할 것입니다. RAG 시스템은 구글 워크스페이스, Notion, GitHub, 리니어와 같은 별도의 소스에서 관련 정보를 찾습니다. 그런 다음 이 데이터를 사용하여 답변을 생성합니다. 최종 인터페이스는 챗봇이 될 것입니다. 직원들은 질문을 하고 정보를 검색할 수 있습니다. 업스테이지에서는 내부용으로 '김솔라'라는 애플리케이션도 개발했는데, 이 블로그에서 그 예로 소개합니다. 

이제 실제로 Solar와 MongoDB Atlas를 살펴보겠습니다!

MongoDB Atlas 및 Solar

RAG 시스템의 핵심은 세 부분으로 구성되어 있습니다. 첫째, 문서를 벡터로 저장하는 벡터 데이터베이스입니다. 둘째, 문서를 벡터에 임베딩하는 임베딩 모델입니다. 셋째, 답을 생성하는 LLM입니다. 업스테이지와 몽고DB는 RAG의 핵심 구성 요소 개발의 일부입니다. Upstage는 문서 벡터화 임베딩과 RAG에 특화된 LLM을 제공합니다. MongoDB는 RAG를 위한 벡터 데이터베이스 기능을 자사의 문서 데이터베이스 Atlas에 통합했습니다.

몽고DB 아틀라스

Atlas는 벡터 검색 기능으로 기존 문서 기반 데이터베이스를 개선하여 많은 이점을 제공합니다. 이 프로젝트는 이러한 장점을 완벽하게 활용했습니다. 이 프로젝트에 특히 유용했던 몇 가지 주요 기능을 소개해드리겠습니다. 이러한 장점은 MongoDB가 벡터 DB일 뿐만 아니라 전체 텍스트 검색이 가능한 문서 기반 데이터베이스이기 때문에 발생합니다.

  • 기존 문서 기반 데이터베이스와 벡터 검색의 장점을 결합한 다양한 기능을 제공합니다.

  • Lucene을 지원합니다.

  • 안정적이고 운영 비용을 절감할 수 있는 완전 관리형 서비스입니다.

  • 익숙하고 유연한 쿼리로 전환 비용을 줄일 수 있습니다.

태양광 임베딩

Solar 임베딩은 32k 컨텍스트를 지원하는 Solar 기반 임베딩 모델입니다. Solar 임베딩은 다른 임베딩 모델보다 성능이 월등히 뛰어납니다. 영어, 한국어, 일본어에 강합니다. 이러한 성능으로 인해 점점 더 많은 고객들이 RAG에서 Solar 임베딩을 채택하고 있습니다. [ → 자세히 알아보기 솔라 임베딩.] 

Solar LLM

Solar LLM은 초고속, 고성능 LLM 모델입니다. 또한 레이아웃 분석 및 접지 확인과 같은 RAG 사용 사례에 유용한 추가 기능도 있습니다. 또한 LangChain과 LlamaIndex는 Solar를 공식 파트너 패키지로 추가했습니다. 이를 통해 이러한 프레임워크 내에서 원활하고 쉽게 사용할 수 있습니다.

RAG 시스템 구축

RAG 시스템 구축

오픈 소스 통합의 Solar 및 Atlas

업스테이지의 Solar 임베딩 및 Solar LLM은 OpenAI 패키지와 호환됩니다. 또한 LangChain과 LlamaIndex에 원활하게 통합됩니다. 아래는 임베딩과 채팅에 LlamaIndex를 사용하는 예시입니다. 자세한 내용은 업스테이지 패키지 설명서를 참조하세요. 업스테이지 랭체인업스테이지 라마 인덱스. 또한 몽고DB 설명서를 참조하십시오. Atlas LangChain아틀라스 라마 인덱스.



라마인덱스에서 Solar 및 몽고DB 설정하기

Solar 및 Atlas는 LlamaIndex와 호환됩니다. Solar 및 Atlas를 LlamaIndex와 함께 사용하려면 아래 그림과 같이 초기 설정을 수행하세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
llama_index.core에서 StorageContext를 가져옵니다.
llama_index.core.settings에서 설정 가져오기
llama_index.llms.upstage에서 Upstage 가져오기
llama_index.embeddings.upstage에서 UpstageEmbedding을 가져옵니다.
llama_index.vector_stores.mongodb에서 몽고DB아틀라스벡터검색을 가져옵니다.
 
 
embed_model = 업스테이지 임베딩(
        api_key =UPSTAGE_API_KEY,
    )
llm = Upstage(api_key =UPSTAGE_API_KEY)
 
Settings.embed_model = embed_model
Settings.node_parser = SemanticSplitterNodeParser(
       buffer_size=1, embed_model =embed_model
   )
 
Settings.llm = llm
 
mongodb_client = pymongo.MongoClient(mongodb_url)
벡터 저장소 = 몽고DB아틀라스벡터검색(
       몽고DB_클라이언트 =몽고DB_클라이언트,
       db_name =db_name,
       컬렉션_이름 =컬렉션_이름,
       인덱스_이름 ="벡터_인덱스",
)
 
storage_context = StorageContext.from_defaults(
    벡터_저장소 =vector_store,
)
cs

솔라 임베딩으로 임베딩하기

RAG 시스템에서는 몇 가지 이유로 문서가 더 작은 덩어리로 분할됩니다. 첫째, 임베딩 모델은 전체 문서가 아닌 보다 정확한 의미를 가진 벡터에 적당한 길이의 텍스트를 임베딩합니다. 둘째, LLM 모델이 정확한 답변을 제공하기 위해서는 불필요한 부분을 제외하고 텍스트의 가장 중요한 부분에 대한 컨텍스트를 제공하는 것이 더 낫습니다. 게다가 토큰 수가 적으면 비용적인 이점도 있습니다.

문서를 청크로 분할하는 전략에는 여러 가지가 있습니다. 문서 유형, 형식 또는 의미론적 내용에 따라 전략이 달라질 수 있습니다. 김솔라에서는 임베딩 유사성을 사용하여 문장 사이의 중단점을 적응적으로 선택하는 시맨틱 청킹을 사용합니다. 이 방법은 의미적으로 유사한 문장 덩어리를 구성합니다.

1
2
3
4
5
6
7
8
9
10
노드 = Settings.node_parser.get_nodes_from_documents(documents, show_progress=True)
 
 
임베딩 = Settings.embed_model.get_text_embedding_batch(
                [배치_노드의 노드에 대한 노드.텍스트],
                show_progress= True
             )
노드의 경우 zip(노드, 임베딩) 임베딩합니다:
    node.embedding = 임베딩
storage_context.vector_store.add(nodes)
cs



Atlas로 색인 생성

벡터 데이터베이스에 문서를 저장할 때 벡터와 메타데이터를 모두 통합했습니다. 메타데이터에는 제목, 작성자, 작성 날짜 등 문서에 대한 추가 정보가 포함되어 있습니다. Atlas는 기본적으로 문서 데이터베이스입니다. 루씬으로 구동되는 다양한 색인 분석기를 제공합니다. 임베딩 벡터 이외의 필드에 대해 작동합니다. '검색'과 '벡터검색'이라는 두 가지 인덱스 유형은 다양한 검색 전략을 지원합니다. 이 프로젝트에서는 벡터 인덱스와 문서 제목 및 작성자에 대한 인덱스를 사용했습니다.


Atlas로 검색

RAG 시스템에서 검색은 사용자의 쿼리와 가장 관련성이 높은 문서를 검색하는 데 중요한 역할을 합니다. Atlas는 Lucene을 사용해 데이터베이스 수준에서 텍스트 검색과 벡터 검색을 모두 제공합니다. Lucene은 MongoDB 쿼리를 사용해 복잡한 문서 검색을 가능하게 합니다. 또한 LangChain 및 LlamaIndex와 같은 LLM 프레임워크와 통합되어 있습니다. 이러한 프레임워크와 함께 사용할 수 있습니다.

이 RAG 시스템에서는 하이브리드 검색 방법을 사용해 문서를 검색했습니다. 먼저 임베디드 벡터를 사용한 벡터 검색과 Atlas의 Lucene을 사용한 bm25 검색을 통해 문서를 필터링합니다. 그런 다음 RRF(상호 순위 융합) 알고리즘을 통해 최종 문서를 선택합니다.

각 방법의 장점과 한계를 평가하면서 라마인덱스와 몽고DB 쿼리를 모두 살펴봤습니다. 후자의 섹션에서는 RAG 시스템이 데이터를 검색하는 방법을 보여줍니다. 여기서는 LlamaIndex와 Atlas 쿼리를 모두 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
색인 = VectorStoreIndex.from_vector_store(
  vector_store =storage_context.vector_store, 
  embed_model =Settings.embed_model
)
 
벡터_리트리버 = 벡터인덱스리트리버(index =index, 유사성_최고_k=7)
 
 
bm25_retriever = 몽고DB아틀라스BM25리트리버(
  몽고DB_클라이언트 =storage_context.vector_store.client,
  db_name =db_name,
  컬렉션_이름 =컬렉션_이름,
  index_name ="title_index",
  text_key ="text",
  유사성_탑_k=7
)
 
리트리버 = 쿼리 퓨전 리트리버(
  검색 =[벡터_리트리버, BM25_리트리버],
  유사성_톱_k=3,
  모드 ="상호_재랭크"
)
 
cs

따라서 Atlas는 대부분의 RAG 프레임워크와 잘 통합되어 있어 사용하기 쉽습니다. 더 복잡한 쿼리가 필요하거나 프레임워크에서 지원하지 않는 기능이 필요한 경우, MongoDB 기본 쿼리를 사용할 수 있습니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
mongodb_client.aggregate(
        [
            {
                # 쿼리의 쿼리Vector 필드에 벡터 임베딩으로 지정된 쿼리에 대한 임베딩 필드를 검색하는 $vectorSearch 단계입니다.
                # 쿼리는 최대 20개의 가장 가까운 이웃을 검색하도록 지정하고 결과를 7개 문서로만 제한합니다. 이 단계에서는 시맨틱 검색에서 정렬된 문서를 결과에 반환합니다.
                "$vectorSearch": {
                    "index": "title_index",
                    "경로": "임베딩",
                    "쿼리 벡터": 업스테이지 임베딩(
                        모델 ="solar-embedding-1-large"
                    ).embed_query(쿼리),
                    "numCandidates": 20,
                    "limit": 7,
                }
            },
            {
                 # 그룹 단계에서는 시맨틱 검색 결과의 모든 문서를 docs라는 필드에 그룹화합니다.
                "$group": {"_id"None"docs": {"$push""$$ROOT"}}
            },
            {
                 # 문서 필드에 있는 문서 배열을 풀고 결과 배열의 문서 위치를 순위라는 필드에 저장하는 $unwind 단계입니다.
                "$unwind": {"path""$docs""includeArrayIndex""rank"}
            },
            {
             
                # 추가 필드 단계에서 결과의 각 문서에 대한 상호 순위 점수를 포함하는 vs_score라는 새 필드를 추가합니다.
                # 여기서 상호 순위 점수는 1.0을 순위, 벡터_페널티 가중치, 상수 값 1의 합으로 나누어 계산합니다.
                "$addFields": {
                    "vs_score": {
                        "$divide": [1.0, {"$add": ["$rank", vector_penalty, 1]}]
                    }
                }
            },
            {
                # 프로젝트 단계에서 결과에 다음 필드만 포함하도록 $project 단계: vs_score, _id, title, text
                "$project": {
                    "vs_score": 1,
                    "_id": "$docs._id",
                    "title": "$docs.title",
                    "text": "$docs.text",
                }
            },
            {
                # 텍스트 검색 결과 추가
                하위 파이프라인에서 이전 단계의 결과를 다음 단계의 결과와 결합하는 # $unionWith 단계를 사용합니다.
                "$unionWith": {
                    "coll": "문서",
                    "파이프라인": [
                        {
                            # 텍스트 필드에 쿼리가 포함된 텍스트를 검색하는 $검색 단계입니다. 이 단계에서는 키워드 검색에서 정렬된 문서를 결과에 반환합니다.
                            "$search": {
                                "index": "text",
                                "phrase": {"query": query, "path""text"},
                            }
                        },
                        {
                            "$limit": 7
                        },
                        {
                            "$group": {"_id"None"docs": {"$push""$$ROOT"}}
                        },
                        {
                            "$unwind": {"path""$docs""includeArrayIndex":                          "rank"}
                        },
                        {
                            # 여기서 상호 순위 점수는 1.0을 순위 값, 전체 텍스트 페널티 가중치, 상수 값 1의 합으로 나누어 계산합니다.
                            "$addFields": {
                                "kws_score": {
                                    "$divide": [
                                        1.0,
                                        {"$add": ["$rank", keyword_penalty, 1]},
                                    ]
                                }
                            }
                        },
                        {
                            "$project": {
                                "kws_score": 1,
                                "_id": "$docs._id",
                                "title": "$docs.title",
                                "text": "$docs.text",
                            }
                        },
                    ],
                }
            },
            {
                # 프로젝트 단계에 다음 필드만 결과에 포함하도록 설정합니다: _id, title, text, vs_score, kws_score
                "$project": {
                    "title": 1,
                    "vs_score": {"$ifNull": ["$vs_score"0]},
                    "kws_score": {"$ifNull": ["$kws_score"0]},
                    "text": 1,
                }
            },
            {
                # 프로젝트 단계에서 vs_score와 kws_score의 합이 포함된 score라는 이름의 필드를 결과에 추가합니다.
                "$project": {
                    "score": {"$add": ["$kws_score""$vs_score"]},
                    "title": 1,
                    "vs_score": 1,
                    "kws_score": 1,
                    "텍스트": 1
                }
            },
            # 정렬 단계를 사용하여 점수별로 결과를 내림차순으로 정렬합니다.
            {"$sort": {"score"-1}},
            # 10개의 결과로만 출력을 제한하려면 $limit 스테이지를 입력합니다.
            {"$limit"3},
        ]
    )
 
 
cs



Solar와 채팅하기

Solar는 사용자의 쿼리와 리트리버가 선택한 청크를 컨텍스트로 수신합니다. 핵심은 주어진 정보만으로 응답할 수 있는 LLM 모델의 능력입니다. 착각을 일으킬 수 있는 과거 지식을 사용해서는 안 됩니다.

1
2
3
4
5
6
7
query_engine = RetrieverQueryEngine.from_args(
    리트리버,
    llm =Settings.llm,
    응답 모드 ="tree_summarize",
    )
 
query_engine.query("태양광 임베딩 모델의 최대 토큰 크기는 얼마입니까?")
cs


김솔라의 사례

결과적으로 김솔라는 Slack 플랫폼 내에서 원활하고 손쉽게 정보에 액세스할 수 있습니다. 사용자는 멘션을 사용하여 봇에게 정보를 요청할 수 있습니다. 봇은 리니어, 구글 드라이브, 깃허브와 같은 소스에서 관련 데이터를 가져옵니다.

결론

이 문서에서는 Solar와 Atlas를 다루었습니다. 두 제품 모두 문서 임베딩, 저장 및 검색, 응답 생성 기능을 수행했습니다. 저희는 이 두 가지 훌륭한 제품을 사용했습니다. 이 두 제품을 통해 프로덕션에 바로 사용할 수 있는 안정적인 RAG 시스템을 구축할 수 있었습니다. 이 RAG 시스템은 업무 처리 속도를 높여 생산성을 향상시켰습니다. 업무용 인공 지능(AGI)을 실현하기 위해 이 프로젝트를 개발하게 되어 기대가 큽니다.

이전 게시물
이전 게시물

[Upstage X DeepLearning.AI] "Pre-training LLMs" Courses Now launched in Collaboration with AI Pioneer Andrew Ng!

다음
다음

Event Recap - Exploring Japan's AI scene with Upstage Solar Mini ja