RAG

RAG

Haystack에서 RAG(Retrieval Augmented Generation, 검색 증강 생성) 파이프라인을 처음부터 어떻게 만드는지 보여드릴게요. 이 페이지는 Haystack으로 RAG 앱을 시작하는 입문서예요. 설치 방법, 첫 RAG 파이프라인 만들기, 그리고 도구를 호출하는 Agent 만들기까지 다룹니다. 여기서는 그중 RAG 파이프라인에 집중해서 살펴볼게요.

첫 RAG 애플리케이션 만들기

우리의 첫 RAG(Retrieval Augmented Generation) 파이프라인을 만들면서, Haystack이 질문에 어떻게 답하는지 직접 확인해 봐요.

먼저 최소 형태의 Haystack을 설치합니다.

pip install haystack-ai

아래 예시들에서는 Haystack Secret을 이용해 API 키를 설정하는 방식을 보여줘요. 선호하는 LLM 제공자(OpenAI, Hugging Face, Anthropic, Amazon Bedrock, Google Gemini 등)를 탭에서 골라 쓰면 됩니다. 더 쉽게 쓰려면 API 키를 환경 변수로 설정해도 돼요.

참고: OpenAIChatGeneratorhaystack-ai 패키지에 포함되어 있어요.

from haystack import Pipeline, Document
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.components.retrievers import InMemoryBM25Retriever
from haystack.document_stores.in_memory import InMemoryDocumentStore
from haystack.components.builders import ChatPromptBuilder
from haystack.utils import Secret
from haystack.dataclasses import ChatMessage

가장 먼저 할 일은 문서를 넣어 둘 곳, 즉 문서 저장소(Document Store)를 만드는 거예요. 그리고 그 안에 검색 대상이 될 세 개의 문서를 넣어 볼게요.

document_store = InMemoryDocumentStore()

document_store.write_documents(
    [
        Document(content="My name is Jean and I live in Paris."),
        Document(content="My name is Mark and I live in Berlin."),
        Document(content="My name is Giorgio and I live in Rome."),
    ],
)

검색된 문서를 바탕으로 답을 만들려면, LLM에게 그 문서들을 어떻게 쓸지 알려주는 프롬프트 템플릿이 필요해요. 아래 템플릿은 시스템 메시지로 "주어진 문서로 질문에 답하라"고 지시하고, 문서 목록과 사용자 질문을 채워 넣는 구조예요. {% for doc in documents %} 같은 부분이 Jinja 템플릿 문법으로, 나중에 실제 문서와 질문이 채워지는 자리예요.

prompt_template = [
    ChatMessage.from_system(
        """
        Given these documents, answer the question.

        Documents:
        {% for doc in documents %}
            {{ doc.content }}
        {% endfor %}
        """,
    ),
    ChatMessage.from_user("{{question}}"),
]

이제 세 가지 핵심 컴포넌트를 준비할게요. 먼저 문서 저장소에서 관련 문서를 찾는 retriever(검색기), 다음으로 채워질 프롬프트를 만드는 prompt_builder, 마지막으로 실제 답을 생성하는 LLM인 llm이에요. required_variables="*"는 프롬프트에 필요한 모든 변수를 채워야 한다는 뜻이에요.

retriever = InMemoryBM25Retriever(document_store=document_store)

prompt_builder = ChatPromptBuilder(template=prompt_template, required_variables="*")

llm = OpenAIChatGenerator(
    api_key=Secret.from_env_var("OPENAI_API_KEY"),
    model="gpt-4o-mini",
)

준비된 컴포넌트들을 하나의 파이프라인에 연결해요. add_component로 각 컴포넌트에 이름을 붙여 등록하고, connect로 실행 흐름을 이어줍니다. 흐름을 살펴보면 — retriever가 찾은 문서가 prompt_builderdocuments 자리에 들어가고, 완성된 프롬프트가 llm으로 전달되는 구조예요.

rag_pipeline = Pipeline()

rag_pipeline.add_component("retriever", retriever)
rag_pipeline.add_component("prompt_builder", prompt_builder)
rag_pipeline.add_component("llm", llm)

rag_pipeline.connect("retriever", "prompt_builder.documents")
rag_pipeline.connect("prompt_builder", "llm")

마지막으로 파이프라인을 실행합니다. 입력에 각 컴포넌트가 필요로 하는 값을 함께 넘겨줘요. retriever에는 검색할 query를, prompt_builder에는 템플릿에 들어갈 question을 주는 거죠.

question = "Who lives in Paris?"

results = rag_pipeline.run(
    {
        "retriever": {"query": question},
        "prompt_builder": {"question": question},
    },
)

print(results["llm"]["replies"])

다음 단계

더 깊이 들어가고 싶다면, 직접 데이터로 완전한 RAG 파이프라인을 만드는 과정을 단계별로 안내하는 "Creating Your First QA Pipeline with Retrieval-Augmentation" 튜토리얼을 확인해 보세요.