LangChain (2)

chelseey·2026년 1월 2일

Embedding

텍스트를 벡터(Vector)로 변환

OpenAIEmbeddings

텍스트를 모델이 처리할 수 있게 토큰화하고,
사전학습 언어모델이 문맥을 반영해 벡터 표현을 생성.

  • 쿼리 임베딩
    사용자의 질문/검색어(쿼리)를 벡터화
  • 문서 임베딩
    저장해둔 문서(청크) 내용을 벡터화

둘의 유사도를 비교해 관련 문서 검색

CacheBackedEmbeddings

같은 텍스트에 대해 임베딩을 다시 계산하지 않도록 결과를 저장(캐시)

작동 방식

  • 문장이 들어오면 이를 해시(Hash)하여 고유한 ID(Key) 생성
  • ID가 저장소(Cache)에 있는지 확인
    있으면: 저장된 벡터를 바로 반환
    없으면: API를 호출해 임베딩을 만들고, 저장소에 저장한 뒤 반환

저장 방식

구분LocalFileStoreInMemoryByteStore
성격영구 보관 (Persistent)비영구 보관 (Volatile)
저장 위치컴퓨터의 하드 디스크 (폴더)컴퓨터의 RAM (메모리)
특징프로그램을 껐다 켜도 데이터가 남아있음프로그램을 끄면 데이터가 즉시 사라짐
용도비용 절감, 운영(Production) 환경, 재사용테스트, 빠른 속도 필요, 일회성 작업

모델이 다르면 벡터도 다르므로,
namespace 에 모델 이름을 명시해 다른 캐시 공간을 쓰도록 격리

HuggingFace Endpoint Embedding

Hugging Face의 엔드포인트에 API로 요청해서 임베딩 벡터를 받아오는 방식

FlagEmbedding(BGE-M3)

  • Dense Vector (밀집 임베딩)
    문서/쿼리를 연속값 벡터로 표현해서 의미 유사도를 계산.
    문맥, 의미 파악

  • Sparse Embedding (Lexical Weight, 희소 임베딩)
    대부분 값이 0인 고차원 벡터로 문서/쿼리를 표현.
    단어별 중요도(lexical weight)를 써서 정확한 단어 매칭을 강화.

  • Multi-Vector (ColBERT 방식)
    문서와 쿼리를 한 벡터가 아니라 토큰(또는 부분) 단위의 여러 벡터로 표현.
    토큰 수준으로 세밀한 매칭 + 문맥 반영

VectorStore

임베딩 벡터를 효율적으로 저장하고 Indexing하는 시스템

  • 확장성 (Scalability)
  • 빠른 검색 속도
  • 의미 검색 (Semantic Search)

Chroma

벡터 저장, 관리, 검색이 가능한 데이터베이스

FAISS

밀집 벡터들 사이의 유사도 검색, 클러스터링을 효율적으로 수행하기 위한 라이브러리

Pinecone

클라우드 벡터 DB

Sparse Encoder

Sparse Vector : 정확한 키워드 일치를 파악
↔︎ Dense Vector : 문맥과 의미를 파악

Retriever

사용자의 질문과 가장 관련된 정보를 벡터 DB에서 검색

검색 방식: Sparse vs Dense

① Sparse Retriever (키워드 중심)
문서/질문을 단어(키워드) 기반의 희소 벡터로 표현
주요 기법:TF-IDF, BM25

② Dense Retriever (의미 중심)
문서/질문을 딥러닝 임베딩으로 연속적인 dense 벡터로 표현
코사인 유사도 등으로 근접한 벡터를 찾음

VectorStore-backed Retriever (벡터스토어 기반 검색기)

벡터 저장소 내부에 구현된 유사도 검색이나 MMR 알고리즘을 활용하여,
질문에 맞는 텍스트를 찾아옴

검색 유형

similarity

  • 유사도 높은 순으로 Top-k 선택

MMR (Maximal Marginal Relevance)

  • 쿼리와 관련 있는 문서를 찾되, 결과가 비슷한 내용으로 중복되는 걸 줄임
  • “쿼리와의 관련성” + “이미 뽑힌 문서들 간 다양성”을 고려해서 Top-k 선택

similarity_score_threshold (유사도 임계값)

  • 쿼리와 덜 관련된 문서를 아예 필터링해서 제외

ContextualCompressionRetriever (문맥 압축 검색기)

검색된 문서를 그대로 반환하지 않고,
query 기준으로 문서를 압축해서 관련 부분만 반환

작동 원리

  • 사용자의 질의를 base retriever에 전달 → 1차 문서 후보를 가져옴
  • 가져온 문서들을 Document Compressor에 통과시킴
  • Compressor가
    문서 내용을 축약하거나(내용 압축)
    문서를 제외해서(필터링)
    최종적으로 더 짧고 더 관련성 높은 문서 목록을 반환

문서 필터링 방식

① LLMChainFilter
초기 검색 결과 문서들 중에서 어떤 문서를 선택할지를 LLM 체인으로 판단

  • 문서 내용을 요약/추출로 바꾸지 않고,
    문서를 선택적으로 반환만 함

② EmbeddingsFilter
쿼리와 각 문서를 임베딩한 뒤, 유사도가 충분히 높은 문서만 남기는 필터

  • 문서마다 LLM을 부르면 비싸고 느림,
    임베딩 유사도 기반으로 저렴하고 빠르게 걸러냄

DocumentCompressorPipeline (문서 압축 파이프라인)

분할 → 중복 제거 → 관련성 필터링

작동 원리

  • 분할 (Split)
    TextSplitter로 문서를 작은 Chunk로 분할
  • 중복 제거 (Dedup)
    EmbeddingsRedundantFilter로 똑같은 말이 적힌 Chunk들을 제거
  • 필터링 (Filter)
    EmbeddingsFilter나 LLMChainFilter로 질문과 관련 없는 Chunk를 버림

EnsembleRetriever (앙상블 검색기)

작동 원리

  • 여러 검색기 통합
    예: BM25 같은 sparse retriever + 임베딩 기반 dense retriever

  • 각 검색기가 뽑아온 문서 후보들을 모은 뒤,
    RRF(Reciprocal Rank Fusion) 방식으로 결과를 합쳐 재순위화

Sparse(BM25/TF-IDF): 키워드/희귀 용어/정확 일치에 강함
Dense (임베딩): 동의어/표현 다양성/문맥 기반 의미 유사도에 강함
→ 상호 보완

LongContextReorder

긴 컨텍스트에 청크를 많이 넣으면 LLM이 중간에 있는 정보를 잘 못 쓰는 경향
→ retrieval로 가져온 문서들을 그대로 LLM에 넣지 않고, 순서를 재배치

작동 원리

  • 덜 관련된 문서는 가운데 배치
  • 더 관련된 문서는 처음과 끝에 배치

ParentDocumentRetriever

검색은 작은 조각(자식)으로 정밀하게 하고,
실제 답변에는 큰 조각(부모)을 가져와 문맥을 채움

작동 원리

  • 문서를 작은 청크(child)로 쪼개 임베딩/인덱싱
  • 검색은 먼저 child 청크에서 관련 조각을 찾음
  • 해당 조각이 속해 있던 부모 문서(parent)를 ID로 찾아서 반환

MultiQueryRetriever

LLM으로 질문을 여러 버전의 쿼리로 확장해 여러 번 검색

MultiVectorRetriever

문서당 여러 표현(embeddings)을 만들어 인덱싱
→ 검색 성능(recall/정확도) 개선

① 작은 청크 임베딩

  • 문서를 더 작은 조각으로 쪼개고, 청크마다 임베딩을 생성
  • 문서의 특정 부분이 질문과 맞으면 더 잘 걸림(세부 탐색)

② 요약 임베딩

  • 문서 내용을 요약한 뒤, 요약 텍스트로 임베딩 생성
  • 문서 전체를 다 보지 않아도 핵심 주제를 빠르게 매칭 가능
    (긴 문서에서 효율적)

③ 가설 질문 활용

  • 문서로부터 예상 질문(가설)을 만들고, 그 질문들을 임베딩
  • 사용자가 문서와 다른 표현으로 물어봐도 잘 매칭될 수 있음

SelfQueryRetriever

자연어 질의를 바탕으로 구조화된 질의(Structured Query)를 생성하여, 단순 의미 검색을 넘어 메타데이터 필터링까지 동시에 수행합니

작동 원리

  • 사용자 자연어 질의 입력

  • Query-constructing LLM chain이 질의를 해석해서
    검색 텍스트(내용 의미 검색용)
    메타데이터 조건(필터)
    를 포함한 StructuredQuery 생성

  • Structured Query Translator가 StructuredQuery를
    실제 VectorStore가 이해하는 필터 문법으로 변환

  • VectorStore에서 (필터 적용) + (유사도 검색)으로 문서 반환

TimeWeightedVectorStoreRetriever

의미 유사도 + 시간 요소(Time Decay) 감쇠를 동시에 반영해 문서를 재정렬하는 검색기

Score=SemanticSimilarity+(1.0decay_rate)hours_passedScore=SemanticSimilarity+(1.0−decay\_rate) ^{hours\_passed}

semantic_similarity : 질문(쿼리)과 문서의 의미적 유사도
decay_rate : 시간이 흐르면서 점수가 줄어드는 속도
hours_passed : 마지막으로 접근한 시점 이후 경과 시간

Reranker

  • 1단계 Retriever : 빠르게 많이 찾기(속도/확장성 우선)
    임베딩 유사도, BM25 같은 “가벼운 계산”
  • 2단계 Reranker : 후보를 정확히 정렬하기(정확도 우선)
    쿼리-문서 쌍마다 무거운 모델로 평가 → 연산량 큼

작동 원리

  • Retriever가 후보 문서 Top-N을 가져옴
  • Reranker가 각 후보에 대해 (쿼리, 문서) 쌍을 구성
  • 트랜스포머 기반 모델로 관련성 점수 계산
  • 점수로 문서 순서를 재정렬
  • 최종 상위 문서(및 점수)를 반환

장점

  • 검색 정확도(특히 상위 결과 품질) 크게 개선
  • 키워드 일치가 약해도 문맥/의미를 더 잘 판단

단점

  • 후보 문서마다 계산하므로 비용/지연 증가
  • 전체 코퍼스에 직접 적용하기엔 비효율적 → 그래서 2단계로 사용

Cross Encoder Reranker

질문과 문서를 따로 임베딩해서 비교하지 않고,
질문 + 문서 를 한 번에 같이 입력으로 넣고
self-attention으로 둘 사이 상호작용을 직접 보면서 관련성 점수 계산

실제 사용 패턴
Bi-encoder로 후보를 빠르게 추출,
Cross-encoder로 정확도를 높임

Cohere Reranker

상용 리랭킹(Re-ranking) API

Retrieval Augmented Generation(RAG)

A. 인덱싱

  • Document Loader
    외부 데이터(파일, 웹, DB 등)에서 문서를 가져와 초기 전처리.
  • 텍스트 분할(Text Splitter)
    긴 문서를 검색·임베딩하기 좋은 청크 단위로 분할.
  • 임베딩(Embedding)
    각 청크의 의미를 벡터로 변환.
    벡터스토어(Vector Store) 저장
    청크 벡터 + 원문/메타데이터를 벡터 DB/인덱스에 저장해 빠르게 검색 가능하게 함.

B. 런타임

  • 검색기(Retriever)
    사용자 질문을 임베딩하고, 벡터스토어에서 유사한 청크 Top-k를 검색.
  • 프롬프트(Prompt)
    검색된 문서(컨텍스트)를 포함해 LLM이 답하게 할 입력 프롬프트를 구성.
  • LLM
    프롬프트(질문 + 검색 컨텍스트)를 바탕으로 답변 생성.
  • 체인(Chain) 생성/실행
    위 단계를 하나의 파이프라인으로 묶어 한 번에 실행되게 구성.

RAPTOR (Recursive Abstractive Processing for Tree-Organized Retrieval)

문서를 트리 구조로 인덱싱해서 검색 성능을 높이는 방식
Leafs : 원본 문서의 텍스트 청크

작동 원리

  • leafs를 임베딩
  • 임베딩 기반으로 클러스터링
  • 각 클러스터를 더 높은 수준(더 추상적인) 요약으로 압축
  • 생성된 요약들을 다시 leaf처럼 취급해 재귀적으로 반복

원문(leaf) → 요약(상위 노드) → 더 상위 요약으로 이어지는 계층적 트리 생성

장점
: 검색 시 상위 요약 노드로 큰 주제를 빠르게 찾고
필요하면 하위 leaf로 내려가 세부 근거를 찾는 식으로 다중 스케일 검색 가능

이전 대화를 기억하는 Chain

① 일반 Chain에 대화기록 추가 (멀티턴 QA 체인)
프롬프트 : 질문 + 대화 기록

  • prompt | llm | StrOutputParser() 로 일반 체인 생성
  • 체인을 RunnableWithMessageHistory로 wrapping

② RAG + RunnableWithMessageHistory (멀티턴 RAG)
프롬프트 : 질문 + 대화 기록 + 검색된 문서

  • 전체 RAG 체인을 RunnableWithMessageHistory로 wrapping

LangChain Expression Language(LCEL)

LangChain에서 LLM 앱 개발을 위한 표준 문법

  • Runnable: LCEL 컴포넌트의 기본 클래스
  • RunnableSequence: A | B | C 처럼 순서대로 이어진 실행
    RunnableParallel(= RunnableMap): 여러 Runnable을 병렬로 실행
  • RunnableLambda: 파이썬 함수를 Runnable로 래핑

RunnableLambda

파이썬의 사용자 정의 함수를 LCEL에서 쓸 수 있게 Runnable로 감싸는 래퍼

ex. prompt | RunnableLambda() | model

LLM 체인 라우팅(RunnableLambda, RunnableBranch)

RunnableLambda

입력값(이전 단계 출력)을 보고 조건에 맞는 경로로 체인을 분기시키는 라우팅 도구
def 함수(): if ... return chain 파이썬 함수

RunnableBranch

형태가 고정: [(cond1, r1), (cond2, r2), ...], default
첫 True 조건을 찾아 그 Runnable 실행

RunnableParallel

여러 Runnable을 병렬로 실행하고, 결과를 dict(map) 형태로 모아주는 Runnable

  1. 입력 데이터 조작 (Formatting)
    프롬프트는 보통 context와 question 두 가지를 필요로 하는데, 사용자는 question 하나만 제시

RunnableParallel 실행 결과 : {"context": ..., "question": ...}

  1. 병렬 처리
    여러 독립적인 작업을 동시에 실행

동적 속성 지정

런타임에 체인을 동적으로 변경하는 방법

① configurable_fields
같은 컴포넌트의 특정 설정값(예: model_name)을 런타임에 바꾸는 방법

② configurable_alternatives
컴포넌트 자체를 후보 중에서 런타임에 선택하는 방법

@chain 데코레이터를 사용하여 Runnable 구성

python 함수 위에 붙이면 RunnableLambda로 변환

사용자 정의 제네레이터(generator)

제너레이터 함수(yield)를 사용하면 스트리밍을 유지 가능

Runtime Arguments 바인딩

LCEL에서 입력/출력 흐름과 무관한 고정 인자를 Runnable에 미리 붙여두고, 실행할 때마다 자동으로 전달되게 하는 기능

폴백(fallback)

실패 시 대체 Runnable/모델/체인으로 자동 전환해 서비스를 안정화하는 메커니즘

  • 주의 : 재시도(Retry) 끄기
    많은 LLM 래퍼는 기본적으로 재시도(retry) 진행
    기본 재시도 동작을 꺼야 fallback 사용 가능

  • 처리해야할 오류를 구체적으로 명시
    특정 예외에만 폴백이 발동되도록 예외 필터링
    ex. exceptions_to_handle

0개의 댓글