🔥알림🔥
① 테디노트 유튜브 - 구경하러 가기!
② LangChain 한국어 튜토리얼 바로가기 👀
③ 랭체인 노트 무료 전자책(wikidocs) 바로가기 🙌

7 분 소요

LangChain을 활용하여 간단하게 네이버 뉴스기사를 바탕으로 Q&A 애플리케이션을 만드는 방법을 다룹니다.

이 튜토리얼은 LangChain의 주요 구성 요소와 그들의 상호작용을 설명하며, 실제 애플리케이션 구축 과정을 안내합니다.

[참고]

이번 튜토리얼은 기본 RAG 편입니다. 따라서, 기본 RAG 에 대하여 이미 숙지하고 계신 분들은 LangChain RAG 파헤치기: 문서 기반 QA 시스템 설계 방법 - 심화편 내용을 확인해 보실 것을 권장드립니다.

주요내용

  • 🛠 LangChain 툴킷을 사용하여 쉽게 Q&A 애플리케이션을 만드는 방법
  • 🔄 질문 처리답변 생성의 두 주요 단계로 구성된 아키텍처
  • 📈 LangSmith를 활용한 성능 모니터링과 오류 분석으로 애플리케이션 개선

개요

LangChain은 질문과 답변(Q&A) 애플리케이션을 구축하는 데 필요한 다양한 구성 요소를 제공하는 툴킷 입니다. 이러한 구성 요소들은 개발자가 텍스트 데이터 소스를 활용하여 고급 Q&A 애플리케이션을 쉽게 만들 수 있도록 돕습니다.

본 튜토리얼에서는 LangChain을 사용하여 간단한 Q&A 애플리케이션을 구축하는 과정 을 소개하고, LangChain의 주요 구성 요소와 이들이 어떻게 상호 작용하는지 살펴보겠습니다. 또한, 애플리케이션 개발 과정에서 LangSmith가 어떻게 도움이 될 수 있는지도 탐구할 것입니다.

LangChain Q&A 아키텍처

LangChain Q&A 애플리케이션의 아키텍처는 크게 질문 처리답변 생성 두 가지 주요 단계로 구성됩니다. 이 과정에서 여러 LangChain 구성 요소가 사용됩니다.

① 질문 처리

질문 처리 단계에서는 사용자의 질문을 받아 이를 처리하고, 관련 데이터를 찾는 작업이 이루어집니다. 이를 위해 다음과 같은 구성 요소들이 필요합니다:

  • 데이터 소스 연결: 질문에 대한 답변을 찾기 위해 다양한 텍스트 데이터 소스에 연결해야 합니다. LangChain은 다양한 데이터 소스와의 연결을 간편하게 설정할 수 있도록 돕습니다.

  • 데이터 인덱싱 및 검색: 데이터 소스에서 관련 정보를 효율적으로 찾기 위해, 데이터는 인덱싱되어야 합니다. LangChain은 인덱싱 과정을 자동화하고, 사용자의 질문과 관련된 데이터를 검색하는 데 필요한 도구를 제공합니다.

② 답변 생성

관련 데이터를 찾은 후에는 이를 기반으로 사용자의 질문에 답변을 생성해야 합니다. 이 단계에서는 다음 구성 요소가 중요합니다:

  • 답변 생성 모델: LangChain은 고급 자연어 처리(NLP) 모델을 사용하여 검색된 데이터로부터 답변을 생성할 수 있는 기능을 제공합니다. 이러한 모델은 사용자의 질문과 검색된 데이터를 입력으로 받아, 적절한 답변을 생성합니다.

LangSmith의 역할

LangSmith는 애플리케이션의 성능을 추적하고 분석하는 데 도움을 주는 도구입니다. Q&A 애플리케이션의 복잡성이 증가함에 따라, LangSmith는 다음과 같은 방법으로 유용하게 사용될 수 있습니다:

  • 성능 모니터링: LangSmith를 사용하여 애플리케이션의 질문 처리 및 답변 생성 성능을 모니터링할 수 있습니다. 이를 통해 애플리케이션의 성능을 지속적으로 개선할 수 있습니다.

  • 오류 분석: LangSmith는 애플리케이션이 잘못된 답변을 생성했을 때, 이의 원인을 분석하는 데 도움을 줍니다. 이를 통해 개발자는 애플리케이션의 오류를 정확하게 파악하고, 이를 수정하는 데 필요한 정보를 얻을 수 있습니다.

실습파일

  • https://github.com/teddylee777/langchain-master/blob/main/07-RAG/RAG-1.ipynb

아키텍처

LangChain 공식문서의 Q&A 소개에서 개요한 대로 전형적인 RAG 애플리케이션을 만들 것입니다.

다음과 같은 두 가지 주요 구성 요소를 가지고 있습니다.

  • 인덱싱: 소스에서 데이터를 수집하고 인덱싱하는 파이프라인입니다. 이 작업은 보통 오프라인에서 발생합니다.

  • 검색 및 생성: 실제 RAG 체인으로, 사용자 쿼리를 실행 시간에 받아 인덱스에서 관련 데이터를 검색한 다음, 그 데이터를 모델에 전달합니다.

RAW 데이터에서 답변을 받기까지의 전체 순서는 다음과 같습니다.

인덱싱

indexing

  1. 로드: 먼저 데이터를 로드해야 합니다. 이를 위해 DocumentLoaders를 사용할 것입니다.

  2. 분할: Text splitters는 큰 Documents를 더 작은 청크로 나눕니다. 이는 데이터를 인덱싱하고 모델에 전달하는 데 유용하며, 큰 청크는 검색하기 어렵고 모델의 유한한 컨텍스트 창에 맞지 않습니다.

  3. 저장: 나중에 검색할 수 있도록 분할을 저장하고 인덱싱할 장소가 필요합니다. 이는 종종 VectorStoreEmbeddings 모델을 사용하여 수행됩니다.

검색 및 생성

retrieval

  1. 검색: 사용자 입력이 주어지면 Retriever를 사용하여 저장소에서 관련 분할을 검색합니다.

  2. 생성: ChatModel / LLM은 질문과 검색된 데이터를 포함한 프롬프트를 사용하여 답변을 생성합니다

환경설정

API KEY 를 설정합니다.

# API 키를 환경변수로 관리하기 위한 설정 파일
from dotenv import load_dotenv

# API 키 정보 로드
load_dotenv()
True

LangChain으로 구축한 애플리케이션은 여러 단계에 걸쳐 LLM 호출을 여러 번 사용하게 됩니다. 이러한 애플리케이션이 점점 더 복잡해짐에 따라, 체인이나 에이전트 내부에서 정확히 무슨 일이 일어나고 있는지 조사할 수 있는 능력이 매우 중요해집니다. 이를 위한 최선의 방법은 LangSmith를 사용하는 것입니다.

LangSmith가 필수는 아니지만, 유용합니다. LangSmith를 사용하고 싶다면, 위의 링크에서 가입한 후, 로깅 추적을 시작하기 위해 환경 변수를 설정해야 합니다.

import os

# 디버깅을 위한 프로젝트명을 기입합니다.
os.environ["LANGCHAIN_PROJECT"] = "RAG TUTORIAL"

# tracing 을 위해서는 아래 코드의 주석을 해제하고 실행합니다.
# os.environ["LANGCHAIN_TRACING_V2"] = true

첫 번째 RAG 기반 QA봇

첫 번째 튜토리얼에는 네이버 뉴스기사의 내용에 대해 질문할 수 있는 뉴스기사 QA 앱 을 구축할 것입니다.

이 가이드에서는 OpenAI 챗 모델과 임베딩, 그리고 Chroma 벡터 스토어를 사용할 것입니다.

하지만 여기서 보여지는 모든 것은 어떤 ChatModel이나 LLM, 임베딩, 그리고 VectorStore 또는 Retriever와도 작동합니다.

먼저 다음의 과정을 통해 간단한 인덱싱 파이프라인과 RAG 체인을 약 20줄의 코드로 구현할 수 있습니다.

라이브러리

  • bs4는 웹 페이지를 파싱하기 위한 라이브러리입니다.

  • langchain은 AI와 관련된 다양한 기능을 제공하는 라이브러리로, 여기서는 특히 텍스트 분할(RecursiveCharacterTextSplitter), 문서 로딩(WebBaseLoader), 벡터 저장(Chroma, FAISS), 출력 파싱(StrOutputParser), 실행 가능한 패스스루(RunnablePassthrough) 등을 다룹니다.

  • langchain_openai 모듈을 통해 OpenAI의 챗봇(ChatOpenAI)과 임베딩(OpenAIEmbeddings) 기능을 사용할 수 있습니다.

import bs4
from langchain import hub
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

웹 페이지의 내용을 로드하고, 텍스트를 청크로 나누어 인덱싱하는 과정을 거친 후, 관련된 텍스트 스니펫을 검색하여 새로운 내용을 생성하는 과정을 구현합니다.

WebBaseLoader는 지정된 웹 페이지에서 필요한 부분만을 파싱하기 위해 bs4.SoupStrainer를 사용합니다.

[참고]

  • bs4.SoupStrainer 는 편리하게 웹에서 원하는 요소를 가져올 수 있도록 해줍니다.

(예시)

bs4.SoupStrainer(
    "div",
    attrs={"class": ["newsct_article _article_body", "media_end_head_title"]}, # 클래스 명을 입력
)

LangChain 의 WebBaseLoader 를 활용하여 URL 로부터 뉴스기사의 본문 내용을 추출합니다.

# 뉴스기사 내용을 로드하고, 청크로 나누고, 인덱싱합니다.
loader = WebBaseLoader(
    web_paths=("https://n.news.naver.com/article/437/0000378416",),
    bs_kwargs=dict(
        parse_only=bs4.SoupStrainer(
            "div",
            attrs={"class": ["newsct_article _article_body",
                             "media_end_head_title"]},
        )
    ),
)
docs = loader.load()
print(f"문서의 수: {len(docs)}")
docs
문서의 수: 1
[Document(page_content="\n출산 직원에게 '1억원' 쏜다…회사의 파격적 저출생 정책\n\n\n[앵커]올해 아이 낳을 계획이 있는 가족이라면 솔깃할 소식입니다. 정부가 저출생 대책으로 매달 주는 부모 급여, 0세 아이는 100만원으로 올렸습니다. 여기에 첫만남이용권, 아동수당까지 더하면 아이 돌까지 1년 동안 1520만원을 받습니다. 지자체도 경쟁하듯 지원에 나섰습니다. 인천시는 새로 태어난 아기, 18살될 때까지 1억원을 주겠다. 광주시도 17살될 때까지 7400만원 주겠다고 했습니다. 선거 때면 나타나서 아이 낳으면 현금 주겠다고 밝힌 사람이 있었죠. 과거에는 표만 노린 '황당 공약'이라는 비판이 따라다녔습니다. 그런데 지금은 출산율이 이보다 더 나쁠 수 없다보니, 이런 현금성 지원을 진지하게 정책화 하는 상황까지 온 겁니다. 게다가 기업들도 뛰어들고 있습니다. 이번에는 출산한 직원에게 단번에 1억원을 주겠다는 회사까지 나타났습니다.이상화 기자가 취재했습니다.[기자]한 그룹사가 오늘 파격적인 저출생 정책을 내놨습니다.2021년 이후 태어난 직원 자녀에 1억원씩, 총 70억원을 지원하고 앞으로도 이 정책을 이어가기로 했습니다.해당 기간에 연년생과 쌍둥이 자녀가 있으면 총 2억원을 받게 됩니다.[오현석/부영그룹 직원 : 아이 키우는 데 금전적으로 많이 힘든 세상이잖아요. 교육이나 생활하는 데 큰 도움이 될 거라 생각합니다.]만약 셋째까지 낳는 경우엔 국민주택을 제공하겠다는 뜻도 밝혔습니다.[이중근/부영그룹 회장 : 3년 이내에 세 아이를 갖는 분이 나올 것이고 따라서 주택을 제공할 수 있는 계기가 될 것으로 생각하고.][조용현/부영그룹 직원 : 와이프가 셋째도 갖고 싶어 했는데 경제적 부담 때문에 부정적이었거든요. (이제) 긍정적으로 생각할 수 있을 것 같습니다.]오늘 행사에서는, 회사가 제공하는 출산장려금은 받는 직원들의 세금 부담을 고려해 정부가 면세해달라는 제안도 나왔습니다.이같은 출산장려책은 점점 확산하는 분위기입니다.법정기간보다 육아휴직을 길게 주거나, 남성 직원의 육아휴직을 의무화한 곳도 있습니다.사내 어린이집을 밤 10시까지 운영하고 셋째를 낳으면 무조건 승진시켜 주기도 합니다.한 회사는 지난해 네쌍둥이를 낳은 직원에 의료비를 지원해 관심을 모았습니다.정부 대신 회사가 나서는 출산장려책이 사회적 분위기를 바꿀 거라는 기대가 커지는 가운데, 여력이 부족한 중소지원이 필요하다는 목소리도 나옵니다.[영상디자인 곽세미]\n\t\t\n", metadata={'source': 'https://n.news.naver.com/article/437/0000378416'})]

RecursiveCharacterTextSplitter는 문서를 지정된 크기의 청크로 나눕니다.

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50)

splits = text_splitter.split_documents(docs)

FAISS 혹은 Chroma와 같은 vectorstore는 이러한 청크를 바탕으로 문서의 벡터 표현을 생성합니다.

# 벡터스토어를 생성합니다.
vectorstore = FAISS.from_documents(documents=splits, embedding=OpenAIEmbeddings())

# 뉴스에 포함되어 있는 정보를 검색하고 생성합니다.
retriever = vectorstore.as_retriever()

vectorstore.as_retriever()를 통해 생성된 검색기는 hub.pull로 가져온 프롬프트와 ChatOpenAI 모델을 사용하여 새로운 내용을 생성합니다.

마지막으로, StrOutputParser는 생성된 결과를 문자열로 파싱합니다.

prompt = hub.pull("rlm/rag-prompt")
prompt
ChatPromptTemplate(input_variables=['context', 'question'], messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['context', 'question'], template="You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\nQuestion: {question} \nContext: {context} \nAnswer:"))])
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)


def format_docs(docs):
    # 검색한 문서 결과를 하나의 문단으로 합쳐줍니다.
    return "\n\n".join(doc.page_content for doc in docs)


# 체인을 생성합니다.
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

이 함수는 rag_chain 객체의 invoke 메서드를 사용하여 사용자의 질문을 처리합니다.

LangSmith Trace 보기

rag_chain.invoke(
    "부영그룹의 출산 장려 정책에 대해 설명해주세요."
)  # 문서에 대한 질의를 입력하고, 답변을 출력합니다.
'부영그룹은 2021년 이후 태어난 직원 자녀에게 1억원을 지원하는 출산 장려 정책을 내놓았습니다. 또한, 셋째까지 낳는 경우에는 국민주택을 제공하기로 했습니다. 이러한 정책은 직원들의 경제적 부담을 줄이고 출산을 장려하기 위한 것입니다.'

LangSmith Trace 보기

rag_chain.invoke(
    "부영그룹은 출산 직원에게 얼마의 지원을 제공하나요?"
)  # 문서에 대한 질의를 입력하고, 답변을 출력합니다.
'부영그룹은 출산 직원에게 1억원의 지원을 제공합니다.'

LangSmith Trace 보기

print(
    rag_chain.invoke("정부의 저출생 대책을 bullet points 형식으로 작성해 주세요.")
)  # 문서에 대한 질의를 입력하고, 답변을 출력합니다.
- 정부가 매달 주는 부모 급여를 0세 아이는 100만원으로 올렸습니다.
- 인천시는 새로 태어난 아기, 18살될 때까지 1억원을 주겠다고 했습니다.
- 한 그룹사는 2021년 이후 태어난 직원 자녀에 1억원씩, 총 70억원을 지원하고 셋째까지 낳는 경우 국민주택을 제공할 것이라고 밝혔습니다.

LangSmith Trace 보기

rag_chain.invoke(
    "부영그룹의 임직원 숫자는 몇명인가요?"
)  # 문서에 대한 질의를 입력하고, 답변을 출력합니다.
'부영그룹의 임직원 숫자는 알 수 없습니다.'

vectorstore 객체의 delete_collection 메서드를 호출하여 컬렉션을 삭제합니다.

이는 데이터 정리 과정에서 사용되며, 특정 데이터셋이나 정보를 저장하는 컬렉션을 제거함으로써 시스템의 저장 공간을 확보하고, 불필요한 데이터로 인한 혼란을 방지합니다.

# 컬렉션을 삭제합니다.
vectorstore.delete_collection()

Reference

본 튜토리얼은 LangChain 튜토리얼 노트북 파일을 참조하여 작성하였습니다.

  • 본 문서의 원 저작권자는 langchain-ai 이며, 코드는 MIT License 에 따라 사용이 허가된 파일입니다.
  • 원문 바로가기

댓글남기기