12.4 LangChain & LlamaIndex 연동

12.4 LlamaIndex를 이용한 문서 기반 Q&A 시스템

앞 절에서는 LangChain을 활용한 RAG(Retrieval-Augmented Generation) 구현 방법을 살펴보았습니다. 이번 절에서는 RAG를 위한 또 다른 매우 강력한 도구인 LlamaIndex(LangChain과 통합도 가능)를 이용하여, 사용자 문서 기반 Q&A 시스템을 구축하는 방법을 상세히 다룹니다.

LlamaIndex는 문서 수집, 인덱싱, 검색 기반 프롬프트 생성, 언어 모델 호출까지 RAG 파이프라인을 구성하는 데 특화된 프레임워크입니다. 이번 절에서는 다음 내용을 중심으로 실습과 함께 구성합니다.

  • LlamaIndex의 전체 구조 이해

  • 문서 수집 및 인덱싱

  • 벡터 기반 검색

  • OpenAI API를 통한 질의응답

  • 실제 서비스 형태로 시스템 통합

12.4.1 LlamaIndex란 무엇인가

LlamaIndex(구 GPT Index)는 LLM 기반 어플리케이션을 위한 데이터 연결 계층(Data Framework)입니다. 주로 아래와 같은 구성 요소를 기반으로 RAG 방식 문서 검색 및 질의 응답 시스템을 구축합니다.

주요 특징:

  • 다양한 문서 포맷 읽기 지원 (.txt, .pdf, .docx 등)

  • 텍스트 분할 및 벡터 임베딩 생성

  • 다양한 벡터 DB 연동 (FAISS, Pinecone, Weaviate 등)

  • 검색 및 프롬프트 자동화

  • LangChain과의 통합도 용이

🔍 키 개념:

  • Document: 텍스트 기반 정보 단위

  • Node: Document를 쪼개 벡터화한 텍스트 청크

  • Index: Node 임베딩 데이터베이스

  • Query Engine: 사용자의 질문을 처리하고 관련 노드 검색 + LLM 응답 생성

12.4.2 설치 및 개발 환경 준비

우선 LlamaIndex와 함께 사용할 주요 패키지를 설치합니다.

pip install llama-index openai faiss-cpu

필요 시 다음도 설치:

pip install PyPDF2 python-docx

또한 OpenAI API 키가 필요하며, 코드 상에서 다음과 같이 설정합니다.

import openai
openai.api_key = "your-api-key"

또는 환경 변수에 설정:

export OPENAI_API_KEY=your-api-key

12.4.3 문서 로딩 및 전처리

LlamaIndex는 다양한 입력 문서를 로딩하는 방법을 제공합니다.

예: 텍스트 파일 로딩

from llama_index import download_loader

SimpleDirectoryReader = download_loader("SimpleDirectoryReader")
documents = SimpleDirectoryReader(input_dir="./docs").load_data()

문서가 폴더로 구성되어 있고 각 문서가 .txt, .pdf, .docx라면 LlamaIndex가 자동으로 처리합니다.

로드 후 LlamaIndex는 내부적으로 문서를 "Document" 객체로 표현합니다.

12.4.4 인덱스 생성: 문서 → 벡터

문서를 검색 가능하게 만들기 위해 우리가 해야 할 다음 단계는 "인덱스 생성"입니다.

from llama_index import VectorStoreIndex

index = VectorStoreIndex.from_documents(documents)

이 코드는 다음 과정을 자동으로 수행합니다:

  • 문서를 작게 분절 (청크 단위 텍스트로 분할)

  • 각 청크를 OpenAI Embedding API로 임베딩

  • 내부 벡터 DB(예: FAISS 또는 기본 인메모리)에 저장

별도 설정 없이도 OpenAI의 text-embedding-ada-002 모델을 사용합니다.

청크 설정을 직접 제어하고 싶다면 다음을 활용할 수 있습니다:

from llama_index.text_splitter import TokenTextSplitter

text_splitter = TokenTextSplitter(chunk_size=512, chunk_overlap=50)
index = VectorStoreIndex.from_documents(documents, text_splitter=text_splitter)

12.4.5 질의 엔진(Query Engine) 구성

인덱스가 생성된 후, 사용자의 질문에 답변을 생성할 수 있습니다. 이를 위해 QueryEngine 객체를 사용합니다:

query_engine = index.as_query_engine()
response = query_engine.query("이 문서에서 회사 주소는 어디인가요?")
print(response)

이 코드는 다음 단계를 포함합니다:

  1. 질문을 임베딩으로 변환

  2. 기존 문서 인덱스에서 가장 유사한 청크 검색

  3. 검색된 청크를 system prompt에 포함시켜 LLM에 전달

  4. LLM이 답변 생성

이때 내부적으로 OpenAI의 gpt-3.5-turbo 또는 gpt-4 모델이 사용됩니다 (환경에 따라 설정 가능).

12.4.6 사용자 지정 프롬프트와 템플릿 구성

기본 프롬프트 외에도 커스텀 프롬프트를 생성하여 질의 응답의 품질을 더욱 개선할 수 있습니다.

from llama_index.prompts.prompts import QuestionAnswerPrompt
from llama_index.query_engine import RetrieverQueryEngine

prompt_template_str = (
    "다음 문서를 참고하여 사용자 질문에 한국어로 답하세요.\n\n"
    "문서:\n{context_str}\n\n"
    "질문:\n{query_str}\n\n"
    "답변:"
)

qa_prompt = QuestionAnswerPrompt(prompt_template_str=prompt_template_str)
query_engine = index.as_query_engine(text_qa_template=qa_prompt)

print(query_engine.query("대표이사는 누구입니까?"))

12.4.7 외부 벡터 DB 연동 (예: FAISS)

기본 인메모리 인덱스를 사용하면 프로덕션에 적합하지 않기 때문에 FAISS, Pinecone 등의 벡터 데이터베이스를 사용하는 것이 일반적입니다.

FAISS 연동 예:

from llama_index.vector_stores.faiss import FaissVectorStore
from llama_index.storage.storage_context import StorageContext
import faiss

faiss_index = faiss.IndexFlatL2(1536)
faiss_store = FaissVectorStore(faiss_index=faiss_index)
storage_context = StorageContext.from_defaults(vector_store=faiss_store)

index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)

이렇게 하면 향후 검색 속도를 빠르게 하고 대규모 데이터셋에도 대응할 수 있습니다.

12.4.8 웹 서비스 형태로 연동하기 (Flask 예제)

간단한 문서 기반 Q&A 웹 서비스를 만들기 위해 Flask를 사용할 수 있습니다.

from flask import Flask, request, jsonify

app = Flask(__name__)
query_engine = index.as_query_engine()

@app.route("/ask", methods=["POST"])
def ask():
    question = request.json["question"]
    response = query_engine.query(question)
    return jsonify({"answer": str(response)})

if __name__ == "__main__":
    app.run(debug=True)

이제 POST /ask 엔드포인트로 질문을 전달하면, 문서를 기반으로 한 응답을 얻을 수 있습니다.

12.4.9 고급 기능: 다중 문서 소스, 메타데이터 필터링

LlamaIndex는 다음 기능도 지원합니다:

  • 문서별 메타데이터 설정 (날짜, 카테고리 등)

  • 특정 조건의 문서만 검색하는 필터링(QueryFilter)

  • 다국어 문서 처리 및 번역 검색 대응

  • PDF, HTML, 웹 페이지, Notion 등 다양한 소스 로더 제공

예를 들어:

document = Document(
    text="이 문서는 내부 인사 규정입니다.",
    metadata={"category": "HR", "published": "2023-10-12"}
)

필터링해서 HR 문서만 검색하게 설정할 수도 있습니다.

12.4.10 정리 및 적용 사례

📌 이 절에서 우리는 다음 내용을 실습을 통해 이해했습니다:

  • 텍스트 문서를 벡터로 인덱싱하고 OpenAI로 질문 응답

  • 다양한 문서 형식을 로딩해 통합

  • 시스템 프롬프트와 검색 구조를 커스터마이징

  • 외부 저장소 연동으로 확장성 확보

  • 간단한 API 형태로 실서비스 구성

🏁 응용 예시:

  • 내부 지침서 기반 챗봇

  • 계약서 검색 및 질의응답 봇

  • 고객 지원 DB 연동 Q&A 시스템

  • 지식 기반 AI 도우미

LlamaIndex는 LangChain과 병행해서 사용할 수도 있으며, 더욱 유연한 문서 기반 AI Q&A 시스템 설계를 가능하게 합니다. 다음 장에서는 프론트엔드 통합을 통해 이러한 시스템을 웹 서비스로 만들어봅니다.

Last updated