LangChain으로 RAG 파이프라인 처음부터 구축하기
지금까지 임베딩, 벡터 유사도 검색, 벡터 DB, pgvector 실습, 청킹 전략까지 RAG를 이루는 조각들을 하나씩 살펴봤습니다. 이번 글에서는 이 조각들을 LangChain으로 엮어서, 문서 업로드부터 질의응답까지 전체 RAG 파이프라인을 하나의 흐름으로 구축해보겠습니다.
1. LangChain이 하는 역할
지금까지 직접 짰던 코드(청킹 → 임베딩 → DB 저장 → 검색 → 프롬프트 조합)를 하나하나 연결하는 게 번거로웠다면, LangChain은 이 단계들을 표준화된 컴포넌트로 제공해서 파이프라인을 훨씬 짧은 코드로 구성할 수 있게 해줍니다.
[직접 구현]
청킹 함수 작성 → 임베딩 API 호출 → DB insert 쿼리 작성
→ 검색 쿼리 작성 → 프롬프트 문자열 조합 → LLM 호출
[LangChain]
TextSplitter → Embeddings → VectorStore → Retriever → Chain
(각 단계가 이미 구현된 컴포넌트로 존재, 조립만 하면 됨)
2. 필요한 패키지 설치
pip install langchain langchain-openai langchain-postgres psycopg2-binary
3. 1단계 - 문서 로드 및 청킹
지난 글에서 다룬 RecursiveCharacterTextSplitter를 그대로 사용합니다.
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
loader = TextLoader("company_policy.txt", encoding="utf-8")
documents = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", ". ", " "]
)
chunks = splitter.split_documents(documents)
print(f"총 {len(chunks)}개의 청크로 분할됨")
4. 2단계 - 임베딩 및 벡터 DB 저장
지난 글에서 직접 psycopg2로 처리했던 임베딩 저장 과정을, LangChain의 PGVector 컴포넌트로 대체합니다.
from langchain_openai import OpenAIEmbeddings
from langchain_postgres import PGVector
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
connection_string = "postgresql+psycopg2://postgres:postgres@localhost:5432/postgres"
vectorstore = PGVector.from_documents(
documents=chunks,
embedding=embeddings,
collection_name="company_policy",
connection=connection_string
)
청크 리스트를 넘기기만 하면, 내부적으로 임베딩 변환과 pgvector 테이블 저장까지 한 번에 처리됩니다.
5. 3단계 - Retriever 구성
벡터스토어에서 유사한 문서를 찾아오는 역할을 Retriever로 감싸줍니다.
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
k=3은 질문과 가장 유사한 청크 3개를 가져오라는 의미입니다. 지난 글에서 SQL로 직접 LIMIT 3를 걸었던 것과 동일한 개념입니다.
6. 4단계 - 프롬프트 템플릿 구성
검색된 문서를 어떤 형태로 LLM에게 전달할지 템플릿으로 정의합니다.
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("""
다음 문서를 참고해서 질문에 답변해줘. 문서에 없는 내용은 지어내지 말고
"관련 문서에서 답을 찾을 수 없습니다"라고 답변해줘.
[참고 문서]
{context}
[질문]
{question}
""")
이 템플릿의 {context} 자리에 검색된 청크들이, {question} 자리에 사용자 질문이 들어갑니다.
7. 5단계 - 전체 체인 연결
Retriever, 프롬프트, LLM을 하나의 체인으로 연결하면 파이프라인이 완성됩니다.
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
llm = ChatOpenAI(model="gpt-4o-mini", 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()
)
retriever | format_docs는 검색된 청크들을 하나의 텍스트로 합치는 과정이고, 그 결과가 context로, 원래 질문이 question으로 프롬프트에 채워진 뒤 LLM을 거쳐 문자열로 반환됩니다.
8. 실행하기
question = "재택근무는 주 몇 회까지 가능해?"
answer = rag_chain.invoke(question)
print(answer)
[출력 예시]
"참고 문서에 따르면, 재택근무는 팀장 승인 하에 주 2회까지 가능합니다."
앞서 다룬 개념들, 즉 임베딩 → 벡터 검색 → 프롬프트 조합 → LLM 답변 생성이라는 흐름이 코드 몇 줄로 압축된 것을 확인할 수 있습니다.
9. 지금까지 직접 구현한 것과 비교
| 단계 | 직접 구현 (7~8편) | LangChain |
|---|---|---|
| 청킹 | 직접 함수 작성 | RecursiveCharacterTextSplitter |
| 임베딩 + 저장 | OpenAI API + psycopg2 insert | PGVector.from_documents() |
| 검색 | SQL 코사인 거리 쿼리 직접 작성 | retriever.invoke() |
| 프롬프트 조합 | 문자열 직접 조립 | ChatPromptTemplate |
| 전체 흐름 연결 | 각 단계를 순서대로 호출 | Chain(|)으로 선언적 연결 |
내부 동작 원리는 지금까지 다룬 내용과 동일하지만, LangChain은 이 단계들을 표준 인터페이스로 추상화해서 컴포넌트를 교체하거나(예: pgvector → Pinecone) 확장하기가 훨씬 쉬워집니다.
10. 정리
- LangChain은 청킹, 임베딩, 벡터 검색, 프롬프트 조합, LLM 호출이라는 RAG의 전 단계를 표준 컴포넌트로 제공해 파이프라인 구성을 단순화합니다.
- Retriever, PromptTemplate, LLM을
|연산자로 연결하는 체인 구조를 이해하면, 각 컴포넌트를 원하는 대로 교체하거나 커스터마이징할 수 있습니다. - 지금까지 직접 구현했던 코드와 대응시켜보면, LangChain이 내부적으로 무엇을 대신 해주는지 명확하게 이해할 수 있습니다.
여기까지가 RAG의 이론과 기본 구축 과정입니다. 이후에는 실제 서비스에 붙일 때 마주치는 문제들, 예를 들어 검색 정확도를 높이는 Reranking, 답변 품질을 평가하는 방법 등을 이어서 다뤄볼 수 있습니다.
'RAG를 활용한 LLM 서비스 구축' 카테고리의 다른 글
| STEP 11 - 답변 품질을 측정하는 방법 (0) | 2026.07.09 |
|---|---|
| STEP 10 - 검색 정확도를 높이는 방법 (0) | 2026.07.08 |
| STEP 8 - 문서 청킹 전략 (0) | 2026.07.07 |
| STEP 7 - pgvector 실습 (0) | 2026.07.07 |
| STEP 6 - 벡터 DB 선택하기 (0) | 2026.07.06 |