LangChain의 가장 큰 장점은 유연성입니다. 일반적인 소프트웨어 개발과 마찬가지로, LLM 애플리케이션을 개발할 때도 여러 개의 LLM 컴포넌트가 서로 유기적으로 작동해야 하며, 각 컴포넌트는 독립적으로 확장 가능해야 합니다.
9장에서 “LangChain을 활용하면 변수만 교체하면 쉽게 작업할 수 있다”고 했던 주장을, 이 장에서 실제로 증명합니다.
70개가 넘는 벡터 저장소
RAG를 활용한 LLM 애플리케이션에서 가장 중요한 것은 벡터 저장소와 LLM입니다. LangChain은 70개가 넘는 벡터 저장소를 추상화하여 클래스로 구현했습니다. 0.1 버전 출시 이후 수많은 벡터 저장소가 추가되었고, LangChain은 오픈소스를 지향하기 때문에 벡터 저장소 공급자가 스스로 파이썬 패키지를 개발해 LangChain과 연동할 수 있도록 구조를 설계했습니다.
요구 사항이 확정되면 여러 벡터 저장소를 테스트하며 장단점을 비교해 볼 필요가 있습니다.
| 벡터 저장소 | 특징 |
|---|---|
| Weaviate, Pinecone | 빠른 retrieval로 잘 알려져 있다 |
| Azure AI Search | 관계형 데이터베이스와 유사하게 벡터 저장소에 여러 열(column) 을 생성하고, 복수의 열을 대상으로 검색할 수 있다 |
Azure AI Search에 대한 설명이 실용적입니다. 하나의 청크가 너무 방대해 검색에 어려움을 겪을 때는, 청크를 요약해 하나의 열로 추가하고 요약된 내용을 대상으로 검색해서 검색의 효율을 높일 수 있습니다. 그리고 흔한 오해를 하나 짚습니다. 많은 사람이 애저가 OpenAI에서 제공하는 모델들만 연동 가능하다고 생각하지만 그렇지 않습니다.
Chroma에서 Pinecone으로
!uv add -q langchain-pinecone
바꿔야 할 것은 vector_store 변수에 할당되는 값뿐입니다.
# 기존 — Chroma
from langchain_chroma import Chroma
vector_store = Chroma.from_documents(
documents=document_list,
embedding=embedding,
collection_name="tax-markdown",
persist_directory="./tax-markdown")
# 변경 — Pinecone
from langchain_pinecone import PineconeVectorStore
vector_store = PineconeVectorStore.from_documents(
document_list,
embedding,
index_name="house-tax-index")
몇 가지 차이가 있습니다.
- Pinecone은 벡터 저장소를 컬렉션 대신 ‘인덱스’ 라는 이름으로 관리합니다.
- 클라우드에 호스팅되므로 회원가입 후 API 키를 발급받아야 합니다.
.env에PINECONE_API_KEY로 저장하면 Pinecone 관련 클래스와 함수에서 별도 선언 없이 쓸 수 있습니다. - 인덱스 생성은 파이썬 SDK로도 가능하지만, 처음 한 번만 하면 되기 때문에 웹페이지에서 생성하는 것이 더 간편합니다.
인덱스를 만들 때 벡터의 길이를 설정해야 합니다. OpenAI의 임베딩 모델은 클릭만으로 간단히 설정할 수 있지만, 코히어나 업스테이지의 임베딩 모델은 해당 모델의 스펙을 확인하고 직접 입력해야 합니다. Pinecone이 기본 설정으로 제공하는 목록에 업스테이지 임베딩이 없으므로, [Custom settings]를 선택하고 업스테이지 임베딩 모델의 벡터 크기인 4,096을 Dimension 항목에 입력합니다.
유사도를 무엇으로 잴 것인가
Pinecone의 또 다른 특징은 유사도 검색 방식을 선택할 수 있다는 것입니다. 코사인 유사도와 점곱 두 가지를 지원합니다.
코사인 유사도
두 벡터 간의 각도를 측정하는 방식입니다.
cos θ = (A · B) / (‖A‖ · ‖B‖)
두 벡터가 이루는 각의 코사인값을 계산해 유사도를 측정하며, 값은 -1에서 1 사이로 나타납니다. 1에 가까울수록 유사합니다. 벡터의 크기에 영향을 받지 않는다는 장점이 있지만 계산 비용이 상대적으로 높다는 단점이 있습니다.
유클리드 거리
두 벡터 간의 실제 거리를 계산하는 방식으로, 일상적으로 쓰는 거리 개념과 같습니다. 값이 작을수록 유사합니다.
직관적이고 계산이 단순하지만 벡터의 크기에 민감해 정규화가 필요합니다. 정규화를 하지 않으면 코사인 유사도로 측정했을 때 같은 값이 나오더라도 벡터의 길이가 짧으면 유사도가 상승하게 됩니다.
Pinecone에서는 2025년 상반기부터 유클리드 거리를 활용한 유사도 검색을 지원하지 않지만, AWS OpenSearch를 비롯한 다양한 벡터 저장소에서 지원합니다.
점곱
두 벡터의 차원별 값을 곱한 후 모두 더하는 방식으로, 방향과 크기 모두를 고려하며 값이 클수록 유사합니다.
계산이 매우 빠르지만 유클리드 거리와는 달리 코사인 유사도 기준으로 동일한 값이 나오더라도 벡터의 길이가 클수록 유사도가 높아지는 문제가 있습니다.
그래서 무엇을 쓰나
따라서 벡터의 크기에 영향을 덜 받는 코사인 유사도를 가장 많이 사용합니다. 하지만 항상 코사인 유사도를 선택하는 것이 답이 아니며, 유스 케이스에 따라 테스트를 거쳐 운영하는 서비스에 가장 적합한 방식을 선택하는 것이 좋습니다.
고밀도 벡터와 희소 벡터
차원을 설정하는 과정에서 벡터의 성격도 이해할 필요가 있습니다. 두 벡터의 가장 큰 차이점은 ‘데이터를 어떻게 표현하는가’와 ‘어떤 방식의 검색에 유리한가’ 입니다.
| 희소 벡터(sparse vector) | 고밀도 벡터(dense vector) | |
|---|---|---|
| 형태 | 대부분이 0으로 채워진 형태 | 0이 아닌 실숫값(float)으로 꽉 채워진 형태 |
| 차원 | 전체 단어 사전의 크기만큼(예: 10만 개 이상) 거대한 차원 | 상대적으로 낮은 차원(예: 768, 4,096)으로 압축 |
| 집중하는 것 | 단어의 존재 여부와 빈도 | 수만 개 단어가 가진 의미 |
| 쓰이는 곳 | tf-idf, BM25 같은 전통적 키워드 검색 알고리즘 | 최신 딥러닝 기반 임베딩 모델 |
| 강점 | 검색어와 정확히 일치하는 단어를 찾는 키워드 매칭(lexical search) | 의미 기반 검색(semantic search) |
앞서 설정한 4,096이라는 차원은 4,096개의 실숫값이 모여 하나의 의미를 형성한다는 뜻입니다. 고밀도 벡터의 가장 큰 장점은 텍스트가 달라도(예: ‘휴대폰’과 ‘스마트폰’) 문맥적 의미가 비슷하다면 벡터 공간상에서 가깝게 위치한다는 점입니다. 덕분에 정확한 단어를 모르더라도 질문의 의도를 파악해 관련 문서를 찾아냅니다.
2장에서 본 키워드 검색과 벡터 검색의 구분이, 여기서는 벡터의 자료 구조 수준에서 다시 설명되는 셈입니다.
OpenAI에서 Claude로
벡터 저장소를 바꿨으니 이번엔 LLM입니다. 앤트로픽 콘솔에서 [Get API Key]로 키를 발급받습니다. 사전에 등록한 카드 정보가 없다면 카드 정보를 입력하고 최소 5달러를 충전해야 합니다.
발급받은 키는 .env에 ANTHROPIC_API_KEY 라는 이름으로 저장합니다. OPENAI_API_KEY, PINECONE_API_KEY처럼 별도로 ChatAnthropic 클래스에 API 키를 지정하지 않아도 Claude 모델을 쓸 수 있습니다.
!uv add -q langchain-anthropic
# from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
# llm = ChatOpenAI(model="gpt-4o", temperature=0) # 기존 gpt-4o는 주석 처리한다.
llm = ChatAnthropic(model="claude-opus-4-20250514", temperature=0)
llm 변수를 수정하고 기존에 선언된 체인을 실행하면 gpt-4o 모델을 실행할 때와 같은 답변을 얻습니다. 공시가격 20억을 합산하고 9억을 공제한 뒤 공정시장가액비율 60%를 적용해 과세표준 6.6억 원을 구하고, 누진세율을 적용하는 흐름이 그대로입니다.
정리하면 이 장에서 바뀐 코드는 이것뿐입니다.
| 교체 대상 | 바뀐 것 |
|---|---|
| 벡터 저장소 (Chroma → Pinecone) | vector_store 할당 한 줄 |
| LLM (OpenAI → Claude) | llm 할당 한 줄 |
이처럼 LangChain을 활용하면, 레고 블록을 조립하는 것처럼 다양한 벡터 저장소와 언어모델을 테스트하면서 운영하고자 하는 서비스에 가장 적합한 조합을 사용할 수 있습니다.
6장에서 나온 ‘레고 블럭’ 비유가 Runnable 수준을 넘어 스택 전체로 확장된 것입니다.
2부를 닫으며 — 그리고 LangGraph
여기까지가 LangChain으로 기본적인 RAG 시스템을 구현하는 방법입니다. 하지만 실제 비즈니스 환경에서는 더 복잡한 워크플로와 의사결정 과정이 필요한 경우가 많습니다.
3부에서 다룰 LangGraph로는 이런 일을 할 수 있습니다.
- 다중 단계의 추론 프로세스 구현
- 여러 AI 에이전트 간의 협업 구현
- 복잡한 의사결정 트리의 자동화
- 에이전트의 행동 모니터링과 제어
책이 2부를 닫는 문장이 인상적입니다.
LangChain에서 LangGraph로의 전환은 단순한 도구의 변경이 아닌, 문제 해결 방식의 패러다임 전환을 의미합니다. LangGraph를 활용하면, 우리는 단순한 질의응답을 넘어서 더 지능적이고 자율적인 시스템을 구축할 수 있게 됩니다.
정리
- LangChain은 70개가 넘는 벡터 저장소를 추상화했고, 공급자가 직접 패키지를 만들어 붙일 수 있는 구조다.
- 벡터 저장소를 갈아 끼우는 비용은
vector_store한 줄, LLM은llm한 줄이다. 9장의 수동 구현과 대비된다. - 유사도 측정은 코사인 유사도가 기본값이지만 정답은 아니다. 점곱은 빠른 대신 벡터 길이에 흔들리고, 유클리드 거리는 정규화가 필요하다.
- 희소 벡터는 키워드, 고밀도 벡터는 의미를 다룬다. 4,096차원은 4,096개의 실숫값이 모여 하나의 의미를 이룬다는 뜻이다.
- 청크가 너무 방대하면 요약본을 별도 열로 만들어 그 열을 검색하는 방법도 있다(Azure AI Search).
- 2부는 여기서 끝난다. 3부의 LangGraph는 도구의 교체가 아니라 문제 해결 방식의 전환이다.