OpenAI 없이 RAG 앱 구축하기 - 2부: Mixtral, Milvus 및 OctoAI
이 블로그는 Yujian Tang과 Thierry Moreau가 작성했습니다.
검색 증강 생성(RAG)은 2023년에 등장한 LLM의 가장 인기 있는 사용 사례입니다. 대부분의 예시는 OpenAI의 GPT LLM으로 RAG를 구축하는 방법을 보여주지만, 이 시리즈에서는 OpenAI 없이 RAG를 구축하는 방법을 다룹니다. Building RAG without OpenAI Part One을 여기에서 확인하세요.
이 튜토리얼은 이 시리즈의 두 번째 편으로, Milvus, OctoAI를 통해 호스팅되는 Mixtral, 그리고 LangChain으로 RAG를 구축하는 내용을 다룹니다. 특히, 이 튜토리얼에서는 Mixtral의 수많은 장점 중 빙산의 일각인 다국어 기능을 공유합니다.
이 블로그에서 다룰 내용:
- MMO(L) 오픈 소스 RAG 기술 스택
- Mixtral
- Milvus
- OctoAI
- LangChain
- RAG 앱 아키텍처
- RAG 도구 설정
- 데이터 선택 및 로드
- OctoAI와 Mixtral로 데이터 쿼리
- 요약
GitHub의 노트북을 확인하세요.
MMO(L) 오픈 소스 RAG 기술 스택
RAG 앱을 구축하는 방법은 많으며, 지난 시장 조사에서 LLM 스택의 50개가 넘는 다양한 도구를 발견했습니다. 이 예시에서는 네 가지에 집중합니다: LLM으로서의 Mixtral, 벡터 데이터베이스로서의 Milvus, LLM과 임베딩 모델을 제공하기 위한 OctoAI, 그리고 오케스트레이터로서의 LangChain입니다. 아키텍처를 자세히 살펴보기 전에, 관련 도구에 대해 조금 알아보겠습니다.
Mixtral
Mixtral 8x7B, 줄여서 “Mixtral”은 선구적인 프랑스 AI 스타트업 Mistral이 출시한 최신 모델입니다. 2023년 12월에 출시되었고(논문은 2024년 1월에 공개), 이전의 기반 대규모 언어 모델인 Mistral 7B를 크게 확장한 모델입니다. Mixtral은 원래 Mistral 7B보다 훨씬 더 강력한 기능을 갖춘 8x7B Sparse Mixture of Experts (SMoE) 언어 모델입니다. 더 크며, 47B 매개변수 중 추론 시 13B 활성 매개변수를 사용하고, 여러 언어, 코드, 32k 컨텍스트 창을 지원합니다. 2024년 초 기준으로 Mixtral은 LLM 리더보드에서 최고 점수를 기록한 오픈 소스 모델입니다.
Milvus
우리 RAG 앱 메모리의 핵심은 벡터 데이터베이스입니다. Milvus는 엔터프라이즈 애플리케이션을 목표로 하는 고확장성 벡터 데이터베이스입니다. 내재된 분산 시스템 구조를 통해 실제 프로덕션 수준의 벡터에 가까워질수록 원활한 확장이 가능합니다. Milvus는 또한 멀티테넌시, 역할 기반 액세스 제어, 고가용성 같은 기타 엔터프라이즈 기능도 제공합니다.
OctoAI
OctoAI는 LLM 모델을 프로덕션 규모로 실행할 수 있는 인프라를 제공합니다. OctoAI는 AI 개발자가 Mixtral 및 Nous Hermes와 같은 커뮤니티 파인튜닝 모델을 포함한 강력한 오픈 소스 모델을 제공하는 OctoAI의 호스팅 모델을 쉽게 통합할 수 있게 해줍니다. 신규 OctoAI 가입자 약 10명 중 9명은 선택한 LLM 모델로 Mixtral을 시작하며, 오늘날 OctoAI의 Mixtral은 고객을 위해 매일 수십억 개의 토큰을 생성합니다. 이 튜토리얼에서는 GPT를 매우 인기 있는 Mixtral 모델로 대체합니다.
LangChain
LangChain은 시장에서 가장 인기 있는 LLM 앱 프레임워크라고 할 수 있습니다. LangChain은 상상할 수 있는 거의 모든 도구와의 통합을 포함합니다. 여러 방식으로 사용할 수 있지만, 이 예시에서는 모든 것을 연결하는 데 사용합니다. LangChain을 통해 Milvus와 OctoAI 엔드포인트를 로드한 다음, 이를 사용해 모든 것을 함께 “체인”으로 연결합니다.
RAG 앱 아키텍처
각 RAG 앱에는 네 가지 핵심 구성 요소가 있습니다: LLM, 벡터 데이터베이스, 임베딩 모델, 오케스트레이터입니다. 그 모든 것의 아래에는 인프라 계층이 자리합니다. 이 설정에서는 LLM으로 Mixtral, 벡터 데이터베이스로 Milvus, 임베딩 모델로 GTE Large, 오케스트레이터로 LangChain, 그리고 GTE Large와 Mixtral을 제공하는 인프라 계층으로 OctoAI를 사용합니다.
RAG 도구 설정하기
RAG 도구를 설정하는 것부터 시작해 보겠습니다. 이 섹션에서는 LLM 및 임베딩을 위한 추론 엔드포인트를 설정하고 벡터 데이터베이스를 실행합니다. 먼저 필수 구성 요소를 설치하는 것부터 시작하겠습니다. Milvus와 함께 작업하려면 pymilvus와 milvus가 필요합니다. 이 예제에서 LangChain 기능을 사용하려면 langchain, sentence-transformers, tiktoken이 필요합니다. 마지막으로 OctoAI의 임베딩 및 텍스트 완성 API와 인터페이스하려면 octoai-sdk도 필요합니다.
이 첫 번째 코드 블록을 사용하여 Milvus 및 OctoAI import를 포함해 LangChain에서 필요한 대부분의 import를 로드합니다. 또한 함수들을 함께 체이닝할 수 있게 해주는 LLMChain 모듈과, LLM에 프롬프트를 전달하는 데 사용하는 PromptTemplate 모듈도 로드합니다. 환경 변수도 메모리에 로드해야 합니다. 이를 수행하는 방법은 많지만, 이 예제에서는 python-dotenv 라이브러리의 load_dotenv를 사용합니다.
# ! pip install pymilvus milvus langchain sentence-transformers tiktoken octoai-sdk python-dotenv
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.llms.octoai_endpoint import OctoAIEndpoint
from langchain_community.embeddings import OctoAIEmbeddings
from langchain_community.vectorstores import Milvus
from dotenv import load_dotenv
import os
load_dotenv()
# the line below is just to show that you need to have your OCTOAI_API_TOKEN
os.environ["OCTOAI_API_TOKEN"] = os.getenv("OCTOAI_API_TOKEN")
다음 단계는 LLM 접근을 초기화하는 것입니다. OctoAI를 사용하면 호스팅된 모델에 쉽게 접근하고 이를 사용 사례에 맞게 커스터마이즈할 수 있습니다. LangChain의 OctoAIEndpoint를 사용하고 모델에 접근하기 위해 엔드포인트(코드에 표시됨)를 전달합니다. 또한 필요한 LLM 파라미터도 전달합니다. 이 예제에서는 모델 이름, 최대 토큰 수(출력 프롬프트의 길이), 그리고 모델에 수행해야 할 작업(예: 시스템 프롬프트)과 출력의 창의성 수준(“presence_penalty”, “temperature”, “top_p”)을 알려주는 기타 파라미터입니다. 임베딩의 경우 OctoAI 엔드포인트 URL만 전달합니다. 기본적으로 GTE Large가 사용됩니다. 시간이 지나면서 더 많은 임베딩 모델이 OctoAI에 추가될 예정입니다.
llm = OctoAIEndpoint(
endpoint_url="https://text.octoai.run/v1/chat/completions",
model_kwargs={
"model": "mixtral-8x7b-instruct-fp16",
"max_tokens": 128,
"presence_penalty": 0,
"temperature": 0.01,
"top_p": 0.9,
"messages": [
{
"role": "system",
"content": "You are a helpful assistant. Keep your responses limited to one short paragraph if possible.",
},
],
},
)
embeddings = OctoAIEmbeddings(endpoint_url="https://text.octoai.run/v1/embeddings")
이것의 마지막 요소는 벡터 데이터베이스입니다. 우리는 벡터 데이터베이스로 Milvus Lite를 사용합니다. Milvus에서 default_server를 import한 다음, start() 함수를 호출하여 서버를 시작합니다.
from milvus import default_server
default_server.start()
데이터 선택 및 로드하기
모든 설정이 완료되었으니 이제 데이터를 로드할 시간입니다. 이 예제의 데이터는 GitHub Repo에서 찾을 수 있습니다. 데이터를 얻고 싶다면, 이는 단순히 Wikipedia에서 스크래핑한 것입니다. 데이터를 확보했다면 이제 벡터 데이터베이스에 로드할 차례입니다. 이 작업에는 LangChain과 Milvus를 사용합니다.
이 문서들을 로드하기 위해 LangChain에서 필요한 두 가지 import는 텍스트 분할기(이 경우 CharacterTextSplitter)와 Document입니다. 이제 전체 데이터 디렉터리를 LangChain 추상화인 “Documents” 목록으로 로드할 수 있습니다. 이 예제에서는 “data”라는 디렉터리를 로드합니다. 또한 Documents 목록을 담기 위한 빈 목록도 생성합니다.
다음으로, 디렉터리의 각 파일을 반복합니다. 파일을 읽어 들이고 텍스트 분할기를 사용해 텍스트를 청크로 나눕니다. 이 예제에서는 청크 크기 512와 청크 오버랩 64를 사용합니다. 이는 보통 적절한 경우가 많기 때문에 선택한 값입니다. 결과가 어떻게 달라지는지 보고 싶다면 원하는 대로 조정해 보세요.
텍스트를 청크로 나누면, 일부 메타데이터와 함께 문서로 저장합니다. 텍스트와 함께 보관하는 메타데이터는 문서의 제목과 청크 번호로, 해당 청크가 문서의 어디에 있는지 알 수 있게 해줍니다.
from langchain.text_splitter import CharacterTextSplitter
from langchain.schema import Document
files = os.listdir("./data")
file_texts = []
for file in files:
with open(f"./data/{file}") as f:
file_text = f.read()
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
chunk_size=512, chunk_overlap=64,
)
texts = text_splitter.split_text(file_text)
for i, chunked_text in enumerate(texts):
file_texts.append(Document(page_content=chunked_text,
metadata={"doc_title": file.split(".")[0], "chunk_num": i}))
Documents가 준비되었으니 이제 벡터 데이터베이스에 삽입할 시간입니다. LangChain의 Milvus 통합을 사용하고 from_documents 함수를 호출합니다. 문서, 임베딩 모델, Milvus Lite 인스턴스의 연결 인수, 그리고 컬렉션 이름을 전달합니다. 그런 다음 as_retriever() 메서드를 호출해 그 위에 LLM을 얹고, 이 기본 RAG 예제에서 Milvus를 벡터 데이터베이스로 사용해 작업을 시작합니다.
vector_store = Milvus.from_documents(
file_texts,
embedding=embeddings,
connection_args={"host": "localhost", "port": default_server.listen_port},
collection_name="cities"
)
retriever = vector_store.as_retriever()
OctoAI와 Mixtral로 데이터 쿼리하기
모든 준비가 끝났습니다. 이제 LangChain을 사용해 퍼즐의 검색 부분을 오케스트레이션할 수 있습니다. 먼저 LangChain에 템플릿을 제공합니다. 프롬프트 템플릿에는 두 개의 변수를 전달해야 합니다. 검색한 컨텍스트와 묻고 싶은 질문입니다. 템플릿 문자열을 f-string처럼 다룬 다음, PromptTemplate의 from_template 함수에 전달할 수 있습니다.
템플릿 다음에는 “chain” 부분을 설정합니다. 컨텍스트를 전달하기 위해 RunnablePassthrough 모듈이 필요하고, 출력을 파싱하기 위해 StrOutputParser가 필요합니다. 그런 다음 체인을 설정합니다. 먼저 LangChain에 컨텍스트와 질문을 어디서 가져올지 알려준 뒤, 이를 프롬프트로 파이프하고, 프롬프트는 LLM으로 파이프되며, 마지막으로 문자열 출력 파서로 파이프되어 파싱됩니다. 질문을 하려면 체인을 간단히 invoke하면 됩니다.
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
prompt = PromptTemplate.from_template(template)
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
chain.invoke("How big is the city of Seattle?")
“How big is the city of Seattle” 예시의 경우, 아래에서 예상 출력을 확인할 수 있습니다. 응답은 정확하며 벡터 데이터베이스에 저장해 둔 Wikipedia 정보로 뒷받침됩니다.
OctoAI를 통해 Mixtral의 다국어 기능 활용하기
Mixtral을 사용하고 있으므로 다국어 전문성과 같은 몇 가지 고유한 기능을 활용해 보겠습니다. OctoAI를 사용할 때 좋은 점은 동일한 엔드포인트에 다른 지시를 적용해 서로 다른 작업을 수행할 수 있다는 것입니다. 이 경우 모델에 영어가 아니라 프랑스어로 답하라고 지시하겠습니다.
# Let's make this a bit more fun and showcase the multilingual capabilities of Mixtal which really outshine other open source models
# Our Vector DB is populated with entries from english text - even the embedding model we're using here, GTE-Large
# works best on english text. However Mixtral has good mutlilingual capabilities in French, German, Spanish and Italian.
# So what we'll do is ask the assistant to only answer in french in the system and user prompt. RAG here is performed based on
# english text, but upon producing the user response, the Mixtral LLM will generate tokens in a different language here (french)
llm = OctoAIEndpoint(
endpoint_url="https://text.octoai.run/v1/chat/completions",
model_kwargs={
"model": "mixtral-8x7b-instruct-fp16",
"max_tokens": 128,
"presence_penalty": 0,
"temperature": 0.1,
"top_p": 0.9,
"messages": [
{
"role": "system",
"content": "You are a helpful assistant who responds in french and not in english.",
},
],
},
)
또한 Mixtral에 프랑스어로 답하도록 요청하기 위해 약간 수정된 템플릿을 사용합니다. 새 템플릿으로 프롬프트를 다시 만들어야 하며, 새 프롬프트, 템플릿, LLM으로 체인도 다시 만들어야 합니다. 체인은 같은 방식으로 호출할 수 있습니다. 우리는 같은 질문을 영어로 하고 프랑스어 응답을 기대합니다.
template = """Answer the question in french based only on the following context:
{context}
Question: {question}
"""
prompt = PromptTemplate.from_template(template)
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
chain.invoke("How big is the city of Seattle?")
응답은 아래 이미지와 비슷하게 보일 것입니다.
요약
이 튜토리얼에서는 OpenAI 없이 RAG를 구축하는 또 다른 방법을 살펴보았습니다. 1부에서는 Symbl.AI를 LLM으로 사용했고, 이번에는 OctoAI에서 호스팅하는 Mistral의 Mixtral을 사용했습니다. 우리가 사용한 RAG 프레임워크의 다른 구성 요소로는 벡터 데이터베이스인 Milvus, 오케스트레이터인 LangChain, 그리고 역시 OctoAI에서 호스팅하는 임베딩 모델인 GTE-Large가 있습니다.
우리는 RAG 도구를 설정하고 예시 데이터로 일부 Wikipedia를 로드했습니다. 그런 다음 LangChain을 사용해 데이터를 Milvus로 읽어 들이고 그 위에 “le big model”을 얹었습니다. 마지막으로 Mixtral의 고유한 기능 중 하나인 여러 언어로 작업할 수 있는 능력을 살펴보는 시간도 가졌습니다. 이 예시에서는 프랑스어를 사용했습니다. 다음에는 다른 언어들도 살펴보겠습니다!
계속 읽기

The Real Bottlenecks in Autonomous Driving — And How AI Infrastructure Can Solve Them
Autonomous driving faces a data bottleneck. Learn how AI-native vector databases like Zilliz solve scale, cost, and insight challenges across AV pipelines.

Zilliz Cloud Enterprise Vector Search Powers High-Performance AI on AWS
Zilliz Cloud on AWS powers secure, scalable, ultra-fast vector search for enterprise AI apps, with BYOC, sub-10ms latency, and zero-DevOps simplicity.

8 Latest RAG Advancements Every Developer Should Know
Explore eight advanced RAG variants that can solve real problems you might be facing: slow retrieval, poor context understanding, multimodal data handling, and resource optimization.



